corkboard 0.1.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.
- data/MIT-LICENSE +20 -0
- data/README.md +44 -0
- data/Rakefile +29 -0
- data/app/assets/images/corkboard/image.png +0 -0
- data/app/assets/javascripts/corkboard.js.erb +45 -0
- data/app/assets/javascripts/corkboard/app/board.js +86 -0
- data/app/assets/javascripts/corkboard/base.js +9 -0
- data/app/assets/javascripts/corkboard/lib/publisher.js +53 -0
- data/app/assets/javascripts/corkboard/lib/weighted_randomizer.js +41 -0
- data/app/assets/stylesheets/corkboard/application.css +88 -0
- data/app/assets/stylesheets/corkboard/responsive.css +93 -0
- data/app/controllers/corkboard/application_controller.rb +15 -0
- data/app/controllers/corkboard/authorizations_controller.rb +65 -0
- data/app/controllers/corkboard/board_controller.rb +10 -0
- data/app/controllers/corkboard/posts_controller.rb +72 -0
- data/app/helpers/corkboard/application_helper.rb +72 -0
- data/app/models/corkboard/authorization.rb +13 -0
- data/app/models/corkboard/post.rb +19 -0
- data/app/models/corkboard/subscription.rb +34 -0
- data/app/views/corkboard/authorizations/index.html.erb +32 -0
- data/app/views/corkboard/board/_filters.html.erb +15 -0
- data/app/views/corkboard/board/_posts.html.erb +10 -0
- data/app/views/corkboard/board/show.html.erb +4 -0
- data/app/views/layouts/corkboard/application.html.erb +14 -0
- data/config/routes.rb +29 -0
- data/db/migrate/01_create_corkboard_authorizations.rb +17 -0
- data/lib/corkboard.rb +191 -0
- data/lib/corkboard/client.rb +51 -0
- data/lib/corkboard/clients/instagram.rb +26 -0
- data/lib/corkboard/engine.rb +27 -0
- data/lib/corkboard/provider.rb +21 -0
- data/lib/corkboard/providers/instagram.rb +29 -0
- data/lib/corkboard/publishers/mock.rb +14 -0
- data/lib/corkboard/publishers/pusher.rb +31 -0
- data/lib/corkboard/service/config.rb +12 -0
- data/lib/corkboard/version.rb +3 -0
- data/lib/generators/corkboard/install_generator.rb +16 -0
- data/lib/generators/corkboard/templates/README +14 -0
- data/lib/generators/corkboard/templates/initializer.rb +45 -0
- data/lib/tasks/corkboard_tasks.rake +4 -0
- metadata +412 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
module Corkboard
|
2
|
+
class ApplicationController < ActionController::Base
|
3
|
+
def auth_admin
|
4
|
+
send(Corkboard.authentication[:admin]) if Corkboard.authentication[:admin]
|
5
|
+
end
|
6
|
+
|
7
|
+
def auth_board
|
8
|
+
send(Corkboard.authentication[:board]) if Corkboard.authentication[:board]
|
9
|
+
end
|
10
|
+
|
11
|
+
def disallow!
|
12
|
+
raise Corkboard::ActionForbidden
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Corkboard
|
2
|
+
class AuthorizationsController < Corkboard::ApplicationController
|
3
|
+
before_filter(:auth_admin, {
|
4
|
+
:except => [:create]
|
5
|
+
})
|
6
|
+
|
7
|
+
# GET /:mount_point/authorizations
|
8
|
+
def index
|
9
|
+
authorizations = Corkboard::Authorization.all
|
10
|
+
providers = authorizations.map(&:provider)
|
11
|
+
available = Corkboard.services.reject { |s| providers.include?(s) }
|
12
|
+
|
13
|
+
render(:index, :locals => {
|
14
|
+
:activated => authorizations,
|
15
|
+
:available => available
|
16
|
+
})
|
17
|
+
end
|
18
|
+
|
19
|
+
# GET /:mount_point/auth/:provider/callback
|
20
|
+
# POST /:mount_point/auth/:provider/callback
|
21
|
+
def create
|
22
|
+
# TODO:
|
23
|
+
# * guard based on "state" param:
|
24
|
+
# if `session['omniauth.state]`, `params[:state]` must match.
|
25
|
+
|
26
|
+
authorization = Corkboard::Authorization.create!(auth_attrs)
|
27
|
+
subscription = Corkboard::Subscription.create!(provider, authorization)
|
28
|
+
|
29
|
+
Corkboard.publish!(subscription.backlog)
|
30
|
+
redirect_to(authorizations_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
# DELETE /:mount_point/auth/:provider
|
34
|
+
def destroy
|
35
|
+
# TODO: resolve the fact that there may be more than one for the same
|
36
|
+
# provider. either disallow multiple, or select the correct one.
|
37
|
+
auth = Corkboard::Authorization.find_by_provider(params[:provider])
|
38
|
+
auth.destroy if auth
|
39
|
+
Corkboard.clear_all!
|
40
|
+
|
41
|
+
redirect_to(authorizations_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def auth_attrs
|
47
|
+
@auth_attrs ||= {
|
48
|
+
:resource_owner => nil, # TODO
|
49
|
+
:provider => auth_params.provider,
|
50
|
+
:uid => auth_params.uid,
|
51
|
+
:token => auth_params.credentials.token,
|
52
|
+
:info => auth_params.info,
|
53
|
+
:extra => auth_params.extra
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def auth_params
|
58
|
+
@auth_params ||= request.env['omniauth.auth']
|
59
|
+
end
|
60
|
+
|
61
|
+
def provider
|
62
|
+
@provider ||= params[:provider]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Corkboard
|
2
|
+
class PostsController < Corkboard::ApplicationController
|
3
|
+
def create
|
4
|
+
send(:"create_for_#{provider}")
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def provider
|
10
|
+
@provider ||= params[:provider]
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_for_instagram
|
14
|
+
instagram_challenge || instagram_callback || instagram_refresh
|
15
|
+
end
|
16
|
+
|
17
|
+
def instagram_challenge
|
18
|
+
if request.get? && params['hub.challenge'].present?
|
19
|
+
render(:text => params['hub.challenge'])
|
20
|
+
return true
|
21
|
+
end
|
22
|
+
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def instagram_callback
|
27
|
+
if request.post?
|
28
|
+
# TODO: cache "friends"
|
29
|
+
# TODO: move to Subscription model
|
30
|
+
client = Corkboard.client(:instagram, { :access_token => instagram_authorization.token })
|
31
|
+
friends = client.user_follows.map(&:username)
|
32
|
+
|
33
|
+
::Instagram.process_subscription(params['_json'].to_json) do |handler|
|
34
|
+
handler.on_tag_changed do |tag, change|
|
35
|
+
# Get "my" recent feed items... includes updates from my "friends".
|
36
|
+
response = client.recent
|
37
|
+
|
38
|
+
# Filter those based on configured "interests".
|
39
|
+
relevant = response.data.select do |entry|
|
40
|
+
friendly = friends.include?(entry.user.username)
|
41
|
+
interesting = (entry.tags.map(&:intern) & Corkboard.settings(:instagram)[:interests]).present?
|
42
|
+
friendly && interesting
|
43
|
+
end
|
44
|
+
|
45
|
+
Corkboard.publish!(relevant)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
head :ok and return true
|
50
|
+
end
|
51
|
+
|
52
|
+
false
|
53
|
+
end
|
54
|
+
|
55
|
+
def instagram_refresh
|
56
|
+
if params[:refresh]
|
57
|
+
client = Corkboard.client(:instagram, { :access_token => instagram_authorization.token })
|
58
|
+
response = client.recent
|
59
|
+
|
60
|
+
Corkboard.publish!(response)
|
61
|
+
|
62
|
+
head :ok and return true
|
63
|
+
end
|
64
|
+
|
65
|
+
false
|
66
|
+
end
|
67
|
+
|
68
|
+
def instagram_authorization
|
69
|
+
@instagram_authorization ||= Corkboard::Authorization.find_by_provider('instagram')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'weighted_randomizer'
|
2
|
+
|
3
|
+
module Corkboard
|
4
|
+
module ApplicationHelper
|
5
|
+
def attributes_for(key, custom = {})
|
6
|
+
resource = custom.delete(:resource)
|
7
|
+
|
8
|
+
attrs = begin
|
9
|
+
if (framework = Corkboard.presentation[:framework]) && framework.present?
|
10
|
+
{ :class => send(:"classes_for_#{framework}", key) }
|
11
|
+
else
|
12
|
+
{ :class => classes_for_plain(key) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attrs.merge!(send(:"data_for_#{key}", resource))
|
17
|
+
|
18
|
+
attrs.tap do |h|
|
19
|
+
custom.each do |name, value|
|
20
|
+
h[name] = [value, h[name]].join(' ')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def classes_for_bootstrap(key)
|
28
|
+
@_classes_for_bootstrap ||= {
|
29
|
+
:filters_list => 'btn-group',
|
30
|
+
:filters_item => 'btn'
|
31
|
+
}
|
32
|
+
|
33
|
+
@_classes_for_bootstrap[key]
|
34
|
+
end
|
35
|
+
|
36
|
+
def classes_for_plain(key)
|
37
|
+
@_classes_for_plain ||= {
|
38
|
+
:filters_list => nil,
|
39
|
+
:filters_item => nil
|
40
|
+
}
|
41
|
+
|
42
|
+
@_classes_for_plain[key]
|
43
|
+
end
|
44
|
+
|
45
|
+
def data_for_filters_list(*)
|
46
|
+
{}
|
47
|
+
end
|
48
|
+
|
49
|
+
def data_for_filters_item(*)
|
50
|
+
{}
|
51
|
+
end
|
52
|
+
|
53
|
+
def data_for_posts_item(post)
|
54
|
+
special = (Corkboard.interests[:scope] + [:all])
|
55
|
+
tags = post[:tags].reject { |t| special.include?(t.intern) }
|
56
|
+
|
57
|
+
{
|
58
|
+
:class => ['entry', randomizer.sample].join(' '),
|
59
|
+
:data => {
|
60
|
+
:eid => post[:eid],
|
61
|
+
:tags => tags.join(' ')
|
62
|
+
}
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def randomizer
|
67
|
+
@randomizer ||= begin
|
68
|
+
WeightedRandomizer.new(Corkboard.presentation[:weights])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Corkboard::Authorization < ActiveRecord::Base
|
2
|
+
@table_name = :corkboard_authorizations
|
3
|
+
|
4
|
+
belongs_to :resource_owner
|
5
|
+
|
6
|
+
attr_accessible :resource_owner, :provider, :uid, :token, :info, :extra
|
7
|
+
serialize :info, Hash
|
8
|
+
serialize :extra, Hash
|
9
|
+
|
10
|
+
def provider
|
11
|
+
read_attribute(:provider).intern
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Corkboard::Post
|
2
|
+
class << self
|
3
|
+
def recent
|
4
|
+
posts = []
|
5
|
+
ids = Corkboard.redis.lrange("corkboard:posts", 0, 100)
|
6
|
+
|
7
|
+
if ids.present?
|
8
|
+
keys = Corkboard.redis.mget(*ids)
|
9
|
+
posts = (Corkboard.redis.mget(*keys) || []).compact
|
10
|
+
|
11
|
+
if posts.present?
|
12
|
+
posts.map! { |post| JSON.parse(post.sub(/^[0-9]+\|/, '')).with_indifferent_access }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
posts
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Corkboard::Subscription
|
2
|
+
class << self
|
3
|
+
def create!(provider, authorization)
|
4
|
+
client = Corkboard.client(provider, { :access_token => authorization.token })
|
5
|
+
subscription = self.new(client)
|
6
|
+
|
7
|
+
# TODO: make non-specific to Instagram.
|
8
|
+
Corkboard.interests[:scope].each do |interest|
|
9
|
+
client.subscribe('tag', { :object_id => "#{interest}" })
|
10
|
+
end
|
11
|
+
|
12
|
+
subscription
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :client
|
17
|
+
|
18
|
+
def initialize(client)
|
19
|
+
@client = client
|
20
|
+
end
|
21
|
+
|
22
|
+
def backlog
|
23
|
+
# TODO: make non-specific to Instagram.
|
24
|
+
friends = client.user_follows.map(&:username)
|
25
|
+
response = client.preload
|
26
|
+
|
27
|
+
# Filter those based on configured "interests".
|
28
|
+
response.data.select do |entry|
|
29
|
+
friendly = friends.include?(entry.user.username)
|
30
|
+
interesting = (entry.tags.map(&:intern) & Corkboard.interests[:scope]).present?
|
31
|
+
friendly && interesting
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<article data-view="corkboard/authorizations/index" class="container">
|
2
|
+
<header>
|
3
|
+
<h1>Setup</h1>
|
4
|
+
</header>
|
5
|
+
|
6
|
+
<section>
|
7
|
+
<h2>Activated Services</h2>
|
8
|
+
<ul>
|
9
|
+
<%- activated.each do |authorization| -%>
|
10
|
+
<li>
|
11
|
+
<%#= debug(authorization) %>
|
12
|
+
<p>
|
13
|
+
<%= authorization.provider %>:
|
14
|
+
<%= authorization.info.nickname %>
|
15
|
+
<%= button_to('unlink', authorization_path(authorization.provider), :method => :delete) %>
|
16
|
+
</p>
|
17
|
+
</p>
|
18
|
+
<%- end -%>
|
19
|
+
</ul>
|
20
|
+
</section>
|
21
|
+
|
22
|
+
<section>
|
23
|
+
<h2>Available Services</h2>
|
24
|
+
<ul>
|
25
|
+
<%- available.each do |provider| -%>
|
26
|
+
<li>
|
27
|
+
<%= provider %> <%= link_to('link', authorization_path(provider)) %>
|
28
|
+
</p>
|
29
|
+
<%- end -%>
|
30
|
+
</ul>
|
31
|
+
</section>
|
32
|
+
</article>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<%- filters = Corkboard.interests[:filters] %>
|
2
|
+
|
3
|
+
<nav class="corkboard filters">
|
4
|
+
<%= content_tag(:ul, attributes_for(:filters_list)) do %>
|
5
|
+
<%= content_tag(:li, attributes_for(:filters_item, :class => :active)) do %>
|
6
|
+
<a href="#all" class="all">all</a>
|
7
|
+
<%- end -%>
|
8
|
+
|
9
|
+
<%- filters.each do |filter| -%>
|
10
|
+
<%= content_tag(:li, attributes_for(:filters_item)) do %>
|
11
|
+
<a href="#<%= filter %>"><%= filter %></a>
|
12
|
+
<%- end -%>
|
13
|
+
<%- end -%>
|
14
|
+
<%- end -%>
|
15
|
+
</nav>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<ul class="corkboard posts">
|
2
|
+
<%- posts.each do |post| -%>
|
3
|
+
<%= content_tag(:li, attributes_for(:posts_item, :resource => post)) do %>
|
4
|
+
<figure>
|
5
|
+
<img src="<%= post[:images][:low_resolution][:url] %>">
|
6
|
+
<p class="caption"><%= (post[:caption] || {})['created_time'] %></p>
|
7
|
+
</figure>
|
8
|
+
<%- end -%>
|
9
|
+
<%- end -%>
|
10
|
+
</ul>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Corkboard</title>
|
5
|
+
<%= stylesheet_link_tag "corkboard/application", :media => "all" %>
|
6
|
+
<%= javascript_include_tag "corkboard/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Corkboard::Engine.routes.draw do
|
2
|
+
match "/",
|
3
|
+
:to => "board#show",
|
4
|
+
:as => :board
|
5
|
+
|
6
|
+
match "/auth",
|
7
|
+
:to => "authorizations#index",
|
8
|
+
:as => :authorizations,
|
9
|
+
:via => [:get]
|
10
|
+
|
11
|
+
# NOTE: This route entry is purely for the sake of generating the desired
|
12
|
+
# url/path helper. In fact, OmniAuth handles the actual request.
|
13
|
+
match "/auth/:action",
|
14
|
+
:to => nil,
|
15
|
+
:as => :authorization,
|
16
|
+
:via => [:get]
|
17
|
+
|
18
|
+
match "/auth/:provider/callback",
|
19
|
+
:to => "authorizations#create",
|
20
|
+
:via => [:get, :post]
|
21
|
+
|
22
|
+
match "/auth/:provider",
|
23
|
+
:to => "authorizations#destroy",
|
24
|
+
:via => [:delete]
|
25
|
+
|
26
|
+
match "/posts/:provider/callback",
|
27
|
+
:to => "posts#create",
|
28
|
+
:via => [:get, :post]
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateCorkboardAuthorizations < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :corkboard_authorizations, :force => true do |t|
|
4
|
+
t.references :resource_owner, :polymorphic => { :default => 'User' }
|
5
|
+
t.string :provider, :null => false
|
6
|
+
t.string :uid, :null => false
|
7
|
+
t.string :token, :null => false
|
8
|
+
t.text :info
|
9
|
+
t.text :extra
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
|
13
|
+
add_index :corkboard_authorizations, [:resource_owner_type, :resource_owner_id],
|
14
|
+
:name => 'index_corkboard_authorizations_on_resource_owner'
|
15
|
+
add_index :corkboard_authorizations, :provider
|
16
|
+
end
|
17
|
+
end
|
data/lib/corkboard.rb
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'corkboard/engine'
|
2
|
+
require 'omniauth'
|
3
|
+
require 'redis'
|
4
|
+
|
5
|
+
module Corkboard
|
6
|
+
class ActionForbidden < StandardError ; end
|
7
|
+
|
8
|
+
autoload :Client, 'corkboard/client'
|
9
|
+
autoload :Provider, 'corkboard/provider'
|
10
|
+
|
11
|
+
module Clients
|
12
|
+
autoload :Instagram, 'corkboard/clients/instagram'
|
13
|
+
end
|
14
|
+
|
15
|
+
module Providers
|
16
|
+
autoload :Instagram, 'corkboard/providers/instagram'
|
17
|
+
end
|
18
|
+
|
19
|
+
module Publishers
|
20
|
+
autoload :Mock, 'corkboard/publishers/mock'
|
21
|
+
autoload :Pusher, 'corkboard/publishers/pusher'
|
22
|
+
end
|
23
|
+
|
24
|
+
module Service
|
25
|
+
autoload :Config, 'corkboard/service/config'
|
26
|
+
end
|
27
|
+
|
28
|
+
# The standard mechanism for configuring Corkboard. Run the following to
|
29
|
+
# generate a fresh initializer with configuration defaults and samples:
|
30
|
+
#
|
31
|
+
# rails generate corkboard:install
|
32
|
+
#
|
33
|
+
def self.setup
|
34
|
+
yield self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public Configuration
|
38
|
+
# ---------------------------------------------------------------------------
|
39
|
+
|
40
|
+
# Redis connection.
|
41
|
+
mattr_accessor :redis
|
42
|
+
@@redis = nil
|
43
|
+
|
44
|
+
# Authentication defaults.
|
45
|
+
@@authentication = {
|
46
|
+
:admin => :disallow!,
|
47
|
+
:board => nil
|
48
|
+
}
|
49
|
+
|
50
|
+
# Authentication configuration.
|
51
|
+
def self.authentication(config = nil)
|
52
|
+
if config
|
53
|
+
@@authentication.merge!(config)
|
54
|
+
end
|
55
|
+
|
56
|
+
@@authentication
|
57
|
+
end
|
58
|
+
|
59
|
+
# Presentation/view defaults.
|
60
|
+
@@presentation = {
|
61
|
+
:title => 'Corkboard',
|
62
|
+
:description => 'Corkboard',
|
63
|
+
:framework => :bootstrap,
|
64
|
+
:weights => { :s => 10, :m => 3, :l => 1 }
|
65
|
+
}
|
66
|
+
|
67
|
+
# Presentation/view configuration.
|
68
|
+
def self.presentation(config = nil)
|
69
|
+
if config
|
70
|
+
@@presentation.merge!(config)
|
71
|
+
end
|
72
|
+
|
73
|
+
@@presentation
|
74
|
+
end
|
75
|
+
|
76
|
+
# Interests defaults.
|
77
|
+
@@interests = {
|
78
|
+
:scope => [],
|
79
|
+
:filters => [],
|
80
|
+
}
|
81
|
+
|
82
|
+
# Interests configuration.
|
83
|
+
def self.interests(config = nil)
|
84
|
+
if config
|
85
|
+
@@interests.merge!(config)
|
86
|
+
end
|
87
|
+
|
88
|
+
@@interests
|
89
|
+
end
|
90
|
+
|
91
|
+
# Enable and configure a publisher.
|
92
|
+
#
|
93
|
+
# config.publisher(:pusher, {
|
94
|
+
# :client_app => 'EXAMPLE',
|
95
|
+
# :client_key => 'EXAMPLE',
|
96
|
+
# :client_secret => 'EXAMPLE'
|
97
|
+
# })
|
98
|
+
#
|
99
|
+
def self.publisher(provider, credentials = nil)
|
100
|
+
@@publisher_configs[provider] = publisher_for(provider).new(credentials)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Providers for enabled publishers.
|
104
|
+
def self.publishers
|
105
|
+
publisher_configs.keys
|
106
|
+
end
|
107
|
+
|
108
|
+
# Publication provider configurations.
|
109
|
+
mattr_accessor :publisher_configs
|
110
|
+
@@publisher_configs = ActiveSupport::OrderedHash.new
|
111
|
+
|
112
|
+
# Enable and configure a service.
|
113
|
+
#
|
114
|
+
# config.service(:instagram,
|
115
|
+
# :client_key => 'EXAMPLE',
|
116
|
+
# :client_secret => 'EXAMPLE'
|
117
|
+
# })
|
118
|
+
#
|
119
|
+
def self.service(provider, *args)
|
120
|
+
config = Corkboard::Service::Config.new(provider, args)
|
121
|
+
@@service_configs[provider] = config
|
122
|
+
end
|
123
|
+
|
124
|
+
# Service provider configurations.
|
125
|
+
mattr_accessor :service_configs
|
126
|
+
@@service_configs = ActiveSupport::OrderedHash.new
|
127
|
+
|
128
|
+
# Providers for enabled services.
|
129
|
+
def self.services
|
130
|
+
service_configs.keys
|
131
|
+
end
|
132
|
+
|
133
|
+
# Publishing methods (will likely move elsewhere)
|
134
|
+
# ---------------------------------------------------------------------------
|
135
|
+
|
136
|
+
def self.client(strategy, options = {})
|
137
|
+
provider_for(strategy).client(options)
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.clear_all!
|
141
|
+
keys = Corkboard.redis.keys("corkboard:*")
|
142
|
+
Corkboard.redis.del(keys) if keys.present?
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.provider_for(key)
|
146
|
+
Corkboard::Providers.const_get(camelcase(key))
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.publisher_for(key)
|
150
|
+
Corkboard::Publishers.const_get(camelcase(key))
|
151
|
+
end
|
152
|
+
|
153
|
+
# TODO: make non-specific to instagram.
|
154
|
+
# TODO: post `data` as a collection (fewer http requests sent)
|
155
|
+
def self.publish!(data)
|
156
|
+
received = Time.now.utc.to_i
|
157
|
+
|
158
|
+
data.reverse.each do |item|
|
159
|
+
eid = item.id
|
160
|
+
key = "corkboard:posts:instagram:#{eid}"
|
161
|
+
entry = Hashie::Mash.new({
|
162
|
+
:eid => eid, # String
|
163
|
+
:caption => item.caption, # Mash [:text]
|
164
|
+
:images => item.images, # Mash [:low_resolution, standard_resolution, :thumbnail]
|
165
|
+
:link => item.link, # String
|
166
|
+
:location => item.location, # Mash
|
167
|
+
:tags => item.tags, # Array
|
168
|
+
:user => item.user # Mash [:id, :username]
|
169
|
+
})
|
170
|
+
|
171
|
+
if Corkboard.redis.setnx(key, "#{received}|#{entry.to_json}")
|
172
|
+
position = Corkboard.redis.incr("corkboard:counters:post")
|
173
|
+
reference = "corkboard:posts:#{position}"
|
174
|
+
|
175
|
+
Corkboard.redis.set(reference, key)
|
176
|
+
Corkboard.redis.lpush("corkboard:posts", reference)
|
177
|
+
Corkboard.redis.ltrim("corkboard:posts", 0, 1000)
|
178
|
+
|
179
|
+
publishers.each do |name|
|
180
|
+
publisher_configs[name].publish!(entry)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
def self.camelcase(value)
|
189
|
+
OmniAuth::Utils.camelize(value.to_s)
|
190
|
+
end
|
191
|
+
end
|