tumblrb 1.0.6 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/Gemfile +6 -3
- data/Gemfile.lock +46 -0
- data/README.mkd +51 -21
- data/lib/tumblr.rb +27 -26
- data/lib/tumblr/api.rb +49 -0
- data/lib/tumblr/blog.rb +97 -0
- data/lib/tumblr/config.rb +25 -0
- data/lib/tumblr/middleware/params.rb +10 -0
- data/lib/tumblr/middleware/parsing.rb +34 -0
- data/lib/tumblr/object.rb +172 -0
- data/lib/tumblr/objects/answer.rb +5 -0
- data/lib/tumblr/objects/audio.rb +6 -0
- data/lib/tumblr/objects/chat.rb +10 -0
- data/lib/tumblr/objects/link.rb +5 -0
- data/lib/tumblr/objects/photo.rb +22 -0
- data/lib/tumblr/objects/post.rb +89 -0
- data/lib/tumblr/objects/text.rb +5 -0
- data/lib/tumblr/objects/user.rb +7 -0
- data/lib/tumblr/objects/video.rb +11 -0
- data/lib/tumblr/query.rb +48 -0
- data/lib/tumblr/quote.rb +2 -18
- data/lib/tumblr/version.rb +1 -1
- data/tumblrb.gemspec +13 -25
- metadata +54 -25
- data/lib/tumblr/answer.rb +0 -21
- data/lib/tumblr/audio.rb +0 -21
- data/lib/tumblr/conversation.rb +0 -35
- data/lib/tumblr/item.rb +0 -110
- data/lib/tumblr/link.rb +0 -21
- data/lib/tumblr/page.rb +0 -39
- data/lib/tumblr/photo.rb +0 -37
- data/lib/tumblr/regular.rb +0 -21
- data/lib/tumblr/user.rb +0 -11
- data/lib/tumblr/video.rb +0 -23
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use ruby-1.9.2-p180@tumblrb
|
data/Gemfile
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
|
-
gem "
|
4
|
-
gem "
|
5
|
-
gem "
|
3
|
+
gem "json", "~> 1.6.0"
|
4
|
+
gem "faraday", "~> 0.7.5"
|
5
|
+
gem "activesupport", "~> 3.1.1"
|
6
|
+
gem "i18n", "~> 0.6.0"
|
6
7
|
gem "addressable"
|
8
|
+
gem 'redis-objects'
|
9
|
+
gem 'will_paginate'
|
7
10
|
|
8
11
|
gemspec
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
tumblrb (1.1.0)
|
5
|
+
activesupport (~> 3.0)
|
6
|
+
addressable (~> 2.2.0)
|
7
|
+
curb (~> 0.7.15)
|
8
|
+
json (~> 1.6.0)
|
9
|
+
nokogiri (~> 1.4.4)
|
10
|
+
redis-objects (~> 0.5.0)
|
11
|
+
will_paginate (~> 3.0.0)
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: http://rubygems.org/
|
15
|
+
specs:
|
16
|
+
activesupport (3.1.1)
|
17
|
+
multi_json (~> 1.0)
|
18
|
+
addressable (2.2.6)
|
19
|
+
curb (0.7.15)
|
20
|
+
faraday (0.7.5)
|
21
|
+
addressable (~> 2.2.6)
|
22
|
+
multipart-post (~> 1.1.3)
|
23
|
+
rack (>= 1.1.0, < 2)
|
24
|
+
i18n (0.6.0)
|
25
|
+
json (1.6.1)
|
26
|
+
multi_json (1.0.3)
|
27
|
+
multipart-post (1.1.3)
|
28
|
+
nokogiri (1.4.7)
|
29
|
+
rack (1.3.5)
|
30
|
+
redis (2.2.2)
|
31
|
+
redis-objects (0.5.2)
|
32
|
+
redis (>= 2.1.1)
|
33
|
+
will_paginate (3.0.2)
|
34
|
+
|
35
|
+
PLATFORMS
|
36
|
+
ruby
|
37
|
+
|
38
|
+
DEPENDENCIES
|
39
|
+
activesupport (~> 3.1.1)
|
40
|
+
addressable
|
41
|
+
faraday (~> 0.7.5)
|
42
|
+
i18n (~> 0.6.0)
|
43
|
+
json (~> 1.6.0)
|
44
|
+
redis-objects
|
45
|
+
tumblrb!
|
46
|
+
will_paginate
|
data/README.mkd
CHANGED
@@ -1,7 +1,14 @@
|
|
1
|
-
Tumblr
|
1
|
+
Tumblr Blogging with Redis
|
2
2
|
====================
|
3
3
|
|
4
|
-
A simple,
|
4
|
+
A simple, blogging "platform" built on Tumblr and Redis.
|
5
|
+
|
6
|
+
Reasoning
|
7
|
+
------------
|
8
|
+
|
9
|
+
Tumblr has a great interface for tagging, writing, and editing posts, but you shouldn't rely on it for being up all the time. You also shouldn't have to worry about Tumblr API integration or cacheing on your own. Tumblrb handles the Tumblr API integration, Redis cacheing, and fetching all with an easy to use interface.
|
10
|
+
|
11
|
+
VOILA!
|
5
12
|
|
6
13
|
Installation
|
7
14
|
------------
|
@@ -19,38 +26,54 @@ to your Gemfile
|
|
19
26
|
Setup
|
20
27
|
------------
|
21
28
|
|
22
|
-
|
29
|
+
Tumblrb Needs a little configuration to work properly. It needs to know your blog, your Tumblr API key, and how to connect to a running Redis server :
|
30
|
+
|
31
|
+
You can do that using the configure method :
|
23
32
|
|
24
|
-
Tumblr.
|
25
|
-
|
33
|
+
Tumblr.configure do |config|
|
34
|
+
config.blog = "thegorgonlab"
|
35
|
+
config.api_key = "YOUR API KEY"
|
36
|
+
config.redis = {:host => "localhost", :port => 6379}
|
37
|
+
end
|
38
|
+
|
39
|
+
Redis configuration takes a string path to a configuration file, a hash of options, or a Redis instance itself. If you don't include a "." in your blog name it will add ".tumblr.com" to the end of what you provide. For information about API keys see the [Tumblr API documentation](http://www.tumblr.com/docs/en/api/v2)
|
26
40
|
|
27
|
-
|
41
|
+
Or if you like having all your configuration in a YAML file, you can put it all in a YAML file under the key `tumblr` and call :
|
42
|
+
|
43
|
+
Tumblr.load_config("/path/to/config.yml")
|
28
44
|
|
29
|
-
|
30
|
-
|
45
|
+
Blog and Redis
|
46
|
+
-----------
|
31
47
|
|
32
|
-
|
48
|
+
Most interactions with the Tumblrb gem happen through the `Blog` class, which handles fetching from Tumblr and caching in Redis.
|
33
49
|
|
34
|
-
|
35
|
-
@posts.next_page => 1
|
36
|
-
@posts.previous_page => nil
|
37
|
-
@posts.each do |post|
|
38
|
-
=render :partial => "post", :object => post
|
39
|
-
end
|
50
|
+
To get started, fetch the posts from Tumblr and cache them in Redis, call `Blog.refresh!`. Every time you write a new post for your Tumblr blog, call this method again to keep Tumblrb up to date.
|
40
51
|
|
41
|
-
|
52
|
+
Afterwards, to access your blog entries use an AREL type interface to access posts.
|
42
53
|
|
43
|
-
|
54
|
+
Blog.tagged_with("technology").per_page(3).page(2).all
|
55
|
+
|
56
|
+
Fetching only happens when array methods are called in the chain.
|
57
|
+
|
58
|
+
Loading posts directly
|
59
|
+
------------
|
44
60
|
|
45
|
-
|
61
|
+
You can also load posts directly using a similar interface :
|
62
|
+
|
63
|
+
Post.page(1).per_page(20).tagged_with("technology").type("text").all
|
64
|
+
|
65
|
+
Or you can load 1 post by Tumblr id :
|
66
|
+
|
67
|
+
Post.find(tumblr_id)
|
46
68
|
|
47
|
-
|
69
|
+
|
70
|
+
Post Classes
|
48
71
|
------------
|
49
72
|
|
50
73
|
Every Tumblr entry type is a separate class so you can do stuff like :
|
51
74
|
|
52
75
|
case post
|
53
|
-
when Tumblr::
|
76
|
+
when Tumblr::Text
|
54
77
|
# Display a regular post
|
55
78
|
when Tumblr::Quote
|
56
79
|
# Display a quote
|
@@ -59,10 +82,17 @@ Every Tumblr entry type is a separate class so you can do stuff like :
|
|
59
82
|
...
|
60
83
|
end
|
61
84
|
|
85
|
+
This makes custom display easier.
|
86
|
+
|
62
87
|
Known Issues
|
63
88
|
------------
|
64
89
|
|
65
|
-
There's no testing or documentation. I know,
|
90
|
+
There's no testing or real documentation. I know, wanna help?
|
91
|
+
|
92
|
+
This isn't the same...
|
93
|
+
------------
|
94
|
+
|
95
|
+
If you were using Tumblrb 1.0, this is completely different. I realize that. However, I didn't think anyone was using that gem and I think this is a better more holistic solution. Sorry for the inconvenience.
|
66
96
|
|
67
97
|
Anything else?
|
68
98
|
------------
|
data/lib/tumblr.rb
CHANGED
@@ -1,33 +1,34 @@
|
|
1
|
-
require '
|
2
|
-
require
|
3
|
-
require 'curl'
|
4
|
-
require 'tumblr/user'
|
5
|
-
require 'tumblr/page'
|
6
|
-
require 'tumblr/item'
|
7
|
-
require 'tumblr/answer'
|
8
|
-
require 'tumblr/audio'
|
9
|
-
require 'tumblr/conversation'
|
10
|
-
require 'tumblr/item'
|
11
|
-
require 'tumblr/link'
|
12
|
-
require 'tumblr/photo'
|
13
|
-
require 'tumblr/quote'
|
14
|
-
require 'tumblr/regular'
|
15
|
-
require 'tumblr/video'
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require(:default)
|
16
3
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
4
|
+
require 'faraday'
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
6
|
+
require 'active_support/inflections'
|
7
|
+
require 'active_support/core_ext/array/extract_options'
|
8
|
+
require "redis/objects"
|
9
|
+
require "redis/set"
|
10
|
+
require "redis/value"
|
11
|
+
require "redis/list"
|
12
|
+
require 'will_paginate/collection'
|
13
|
+
require 'tumblr/config'
|
14
|
+
require 'tumblr/query'
|
15
|
+
require 'tumblr/api'
|
16
|
+
require 'tumblr/object'
|
17
|
+
require 'tumblr/blog'
|
18
|
+
require 'tumblr/middleware/params'
|
19
|
+
require 'tumblr/middleware/parsing'
|
20
|
+
|
21
|
+
module Tumblr
|
22
|
+
def self.configure(&block)
|
23
|
+
@config = Config.new
|
24
|
+
block.call(@config)
|
24
25
|
end
|
25
26
|
|
26
|
-
def self.
|
27
|
-
@
|
27
|
+
def self.config
|
28
|
+
@config
|
28
29
|
end
|
29
30
|
|
30
|
-
def self.
|
31
|
-
@
|
31
|
+
def self.load_config(path)
|
32
|
+
@config.load(path)
|
32
33
|
end
|
33
34
|
end
|
data/lib/tumblr/api.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Tumblr
|
2
|
+
class Api
|
3
|
+
class Exception < IOError; end
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def connection
|
7
|
+
unless @connection
|
8
|
+
@connection = ::Faraday.new(:url => host) do |builder|
|
9
|
+
builder.use ::Faraday::Request::UrlEncoded
|
10
|
+
builder.use ::Faraday::Response::Logger
|
11
|
+
builder.use Tumblr::Params
|
12
|
+
builder.use Tumblr::Parsing
|
13
|
+
builder.adapter ::Faraday.default_adapter
|
14
|
+
end
|
15
|
+
end
|
16
|
+
@connection
|
17
|
+
end
|
18
|
+
|
19
|
+
def get(method, params={})
|
20
|
+
response = nil
|
21
|
+
5.times do |attempt|
|
22
|
+
response = connection.get path(method, params)
|
23
|
+
break if response.success?
|
24
|
+
end
|
25
|
+
if response && response.success?
|
26
|
+
response
|
27
|
+
else
|
28
|
+
error = response ? "#{response["status"]}: #{response["message"]}" : "Something went wrong"
|
29
|
+
raise Tumblr::Api::Exception.new(error)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def url(method='', params={})
|
34
|
+
"#{host}#{path(method, params)}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def host
|
38
|
+
"http://api.tumblr.com"
|
39
|
+
end
|
40
|
+
|
41
|
+
def path(method="", params={})
|
42
|
+
uri = Addressable::URI.new
|
43
|
+
params.keys.each { |key| params[key] = params[key].to_s }
|
44
|
+
uri.query_values = params
|
45
|
+
"/v2/blog/#{Tumblr.config.blog}/#{method}?#{uri.query}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/tumblr/blog.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
module Tumblr
|
2
|
+
class Blog < Object
|
3
|
+
extend Query::DelegateMethods
|
4
|
+
|
5
|
+
STORAGE_KEY = "gorgon:blog:entries"
|
6
|
+
string_attribute :title, :name, :description
|
7
|
+
numeric_attribute :posts, :likes
|
8
|
+
boolean_attribute :ask, :ask_anon
|
9
|
+
date_attribute :updated
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :page_size
|
13
|
+
|
14
|
+
def page_size
|
15
|
+
@page_size ||= 3
|
16
|
+
end
|
17
|
+
|
18
|
+
def clear!
|
19
|
+
Tumblr.config.redis.keys("#{STORAGE_KEY}:*").each { |key| Tumblr.config.redis.del(key) }
|
20
|
+
index.clear
|
21
|
+
end
|
22
|
+
|
23
|
+
def refresh!(options={})
|
24
|
+
clear!
|
25
|
+
|
26
|
+
page = options[:min] || 1
|
27
|
+
per_page = options[:per_page] || 100
|
28
|
+
max_page = options[:max]
|
29
|
+
|
30
|
+
loop do
|
31
|
+
posts = Post.per_page(per_page).page(page)
|
32
|
+
if posts.length > 0
|
33
|
+
page = posts.next_page
|
34
|
+
posts.each do |post|
|
35
|
+
index << post.slug
|
36
|
+
post.commit
|
37
|
+
end
|
38
|
+
break if page.nil? || (max_page && page > max_page)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def entries(slugs=nil)
|
45
|
+
slugs ||= index
|
46
|
+
entries = slugs.collect { |slug| entry(slug) }
|
47
|
+
entries.compact!
|
48
|
+
entries
|
49
|
+
end
|
50
|
+
|
51
|
+
def index
|
52
|
+
@index ||= Redis::List.new(STORAGE_KEY, Tumblr.config.redis)
|
53
|
+
end
|
54
|
+
|
55
|
+
def entry(slug)
|
56
|
+
value = Redis::Value.new("#{STORAGE_KEY}:#{slug}", Tumblr.config.redis)
|
57
|
+
Tumblr::Object.unserialize(value.value)
|
58
|
+
end
|
59
|
+
|
60
|
+
def find(slug)
|
61
|
+
entry(slug)
|
62
|
+
end
|
63
|
+
|
64
|
+
def tags
|
65
|
+
Redis::Set.new("#{STORAGE_KEY}:tags", Tumblr.config.redis)
|
66
|
+
end
|
67
|
+
|
68
|
+
def page(n)
|
69
|
+
query.page([1, n.to_i].max)
|
70
|
+
end
|
71
|
+
|
72
|
+
def tagged_with(tag)
|
73
|
+
query.tagged_with(tag)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def query
|
79
|
+
@query ||= Query.new(:page, :per_page, :tagged_with) do |params|
|
80
|
+
if tag = params[:tagged_with]
|
81
|
+
slugs = Redis::List.new("#{STORAGE_KEY}:tags:#{tag.downcase.underscore}", Tumblr.config.redis)
|
82
|
+
else
|
83
|
+
slugs = index
|
84
|
+
end
|
85
|
+
page_size = params[:per_page] || self.page_size
|
86
|
+
if page = params[:page]
|
87
|
+
WillPaginate::Collection.create(page, page_size, slugs.count) do |pager|
|
88
|
+
pager.replace entries(slugs[pager.offset, pager.per_page]).to_a
|
89
|
+
end
|
90
|
+
else
|
91
|
+
entries(slugs).to_a
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Tumblr
|
2
|
+
class Config
|
3
|
+
attr_accessor :blog, :api_key, :redis
|
4
|
+
|
5
|
+
def load(path)
|
6
|
+
config = YAML.load_file(path)["tumblr"]
|
7
|
+
self.blog = config["blog"]
|
8
|
+
self.api_key = config["api_key"]
|
9
|
+
self.redis = config["redis"]
|
10
|
+
end
|
11
|
+
|
12
|
+
def redis=(value)
|
13
|
+
value = YAML.load_file(value)["redis"] rescue nil if value.kind_of?(String) && File.exists?(value)
|
14
|
+
value = Redis.new(value) if value.kind_of?(Hash)
|
15
|
+
unless value.kind_of?(Redis)
|
16
|
+
raise ArgumentError, "Tumblr redis accepts a Redis object, path to a YAML file, or hash of Redis options"
|
17
|
+
end
|
18
|
+
@redis = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def blog=(value)
|
22
|
+
@blog = (blog =~ /\./) ? blog : "#{value}.tumblr.com"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Tumblr
|
2
|
+
class Parsing < ::Faraday::Response::Middleware
|
3
|
+
def parse_body(body)
|
4
|
+
case body
|
5
|
+
when ''
|
6
|
+
nil
|
7
|
+
when 'true'
|
8
|
+
true
|
9
|
+
when 'false'
|
10
|
+
false
|
11
|
+
else
|
12
|
+
JSON.parse(body) rescue nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_complete(env)
|
17
|
+
parsed = parse_body(env[:body])
|
18
|
+
|
19
|
+
if parsed && parsed.kind_of?(Hash) && parsed["meta"]
|
20
|
+
env[:status] = parsed["meta"]["status"]
|
21
|
+
env[:message] = parsed["meta"]["msg"]
|
22
|
+
env[:body] = Tumblr::Object.objectify(parsed["response"])
|
23
|
+
else
|
24
|
+
handle_formatting_error(env)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def handle_formatting_error(env)
|
30
|
+
env[:status] = 500
|
31
|
+
env[:message] = "Poorly formatted response"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|