corkboard 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|