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 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 "activesupport", "~> 3.0"
4
- gem "nokogiri", "~> 1.4.4"
5
- gem "curb", "~> 0.7.15"
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 API Wrapper
1
+ Tumblr Blogging with Redis
2
2
  ====================
3
3
 
4
- A simple, easy to use ruby wrapper for the Tumblr API.
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
- Add
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.user = Tumblr::User.new 'email@email.com', 'password'
25
- Tumblr.blog = 'myblogname'
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
- into your initialization
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
- Fetching
30
- ------------
45
+ Blog and Redis
46
+ -----------
31
47
 
32
- Just like `will_paginate` :
48
+ Most interactions with the Tumblrb gem happen through the `Blog` class, which handles fetching from Tumblr and caching in Redis.
33
49
 
34
- @posts = Tumblr::Item.paginate(:page => 1, :per_page => 20)
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
- In addition to any paging parameters, `paginate` also accepts all the documented params from the [Tumblr API](http://www.tumblr.com/docs/en/api).
52
+ Afterwards, to access your blog entries use an AREL type interface to access posts.
42
53
 
43
- You can also fetch the last post with
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
- Tumblr::Item.first
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
- Item Classes
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::Regular
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, I'm on it.
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 'addressable/uri'
2
- require 'nokogiri'
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
- module Tumblr
18
- def self.blog=(blog)
19
- @blog = (blog =~ /\./) ? blog : "#{blog}.tumblr.com"
20
- end
21
-
22
- def self.blog
23
- @blog
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.user
27
- @user
27
+ def self.config
28
+ @config
28
29
  end
29
30
 
30
- def self.user=(value)
31
- @user = value
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
@@ -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,10 @@
1
+ module Tumblr
2
+ class Params < ::Faraday::Middleware
3
+ def call(env)
4
+ params = env[:url].query_values || {}
5
+ env[:url].query_values ||= {}
6
+ env[:url].query_values = { 'api_key' => Tumblr.config.api_key }.merge!(params)
7
+ @app.call env
8
+ end
9
+ end
10
+ 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