jekyll-twitter-plugin 1.4.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/.travis.yml +0 -1
- data/README.md +92 -82
- data/jekyll-twitter-plugin.gemspec +5 -6
- data/lib/jekyll-twitter-plugin.rb +141 -121
- data/media/embedded-grid.png +0 -0
- data/media/embedded-moment.png +0 -0
- data/media/embedded-timeline.png +0 -0
- data/media/embedded-tweet.png +0 -0
- data/spec/api_request_spec.rb +82 -0
- data/spec/integration_tests.rb +10 -3
- data/spec/spec_helper.rb +1 -0
- data/spec/support/shared_contexts.rb +5 -4
- data/spec/twitter_tag_spec.rb +117 -112
- metadata +22 -17
- data/spec/oembed_spec.rb +0 -103
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c1b655f7a4220868b0d2cd098cba068f4956e68
|
4
|
+
data.tar.gz: a2c010f3a7d65bd5c9fe5e2af53971fa25ca7105
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f83e487aac6acb23ff716b6384914d9f881abdbcddc249418bfa471702b918d5efc39700a943beae3eb1cf37513da84ef7fa2fa41f5a437df150b4e1b8be2f3
|
7
|
+
data.tar.gz: 969e54b10b302987d5a8f40d64a0e545216ff587e68f517bd3b510e2f610453f0cd7afa2109842b98e53dd3825b5efab2b0747c983b2e08759c58db807779914
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,36 +1,61 @@
|
|
1
|
-
|
1
|
+
<img alt="Screenshot embedded tweet" src="https://raw.githubusercontent.com/rob-murray/jekyll-twitter-plugin/master/media/embedded-tweet.png" align="right" />
|
2
2
|
|
3
|
-
|
3
|
+
# jekyll-twitter-plugin
|
4
|
+
|
5
|
+
A Liquid tag plugin for Jekyll blogging engine that embeds Tweets, Timelines and more from Twitter API.
|
4
6
|
|
5
7
|
[![Build Status](https://travis-ci.org/rob-murray/jekyll-twitter-plugin.svg?branch=master)](https://travis-ci.org/rob-murray/jekyll-twitter-plugin)
|
6
8
|
[![Gem Version](https://badge.fury.io/rb/jekyll-twitter-plugin.svg)](http://badge.fury.io/rb/jekyll-twitter-plugin)
|
7
9
|
|
10
|
+
---
|
8
11
|
|
9
|
-
### Description
|
10
12
|
|
11
|
-
|
13
|
+
## Description
|
12
14
|
|
13
|
-
|
15
|
+
**jekyll-twitter-plugin** is a Liquid tag plugin for [Jekyll](http://jekyllrb.com/) that enables Twitter content to be used in any pages generated by Jekyll, content is fetched from the [Twitter Publish platform](https://publish.twitter.com).
|
14
16
|
|
15
|
-
|
17
|
+
The [Publish platform](https://publish.twitter.com) allows Twitter users to curate content for display outside of Twitter, you can display many different types of content with the familiar Twitter styling. We use this API and allow any customisation of the content that is accepted by the Twitter publish platform.
|
16
18
|
|
19
|
+
> You can now embed any Twitter content in your Jekyll powered blog!
|
17
20
|
|
18
|
-
###
|
21
|
+
### Here are a few examples
|
19
22
|
|
20
|
-
|
23
|
+
#### Tweet
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
25
|
+
An example of a Tweet - `{% twitter https://twitter.com/rubygems/status/518821243320287232 %}`
|
26
|
+
|
27
|
+
![Embedded tweet](https://raw.githubusercontent.com/rob-murray/jekyll-twitter-plugin/master/media/embedded-tweet.png "Screenshot of embedded tweet")
|
28
|
+
|
29
|
+
#### Timeline
|
30
|
+
|
31
|
+
An example of a Timeline - `{% twitter https://twitter.com/jekyllrb maxwidth=500 limit=5 %}`
|
32
|
+
|
33
|
+
![Embedded timeline](https://raw.githubusercontent.com/rob-murray/jekyll-twitter-plugin/master/media/embedded-timeline.png "Screenshot of embedded timeline")
|
34
|
+
|
35
|
+
#### Grid Timeline
|
36
|
+
|
37
|
+
An example of a Grid Timeline - `{% twitter https://twitter.com/TwitterDev/timelines/539487832448843776 limit=5 widget_type=grid maxwidth=500 %}`
|
38
|
+
|
39
|
+
![Embedded Grid Timeline](https://raw.githubusercontent.com/rob-murray/jekyll-twitter-plugin/master/media/embedded-grid.png "Screenshot of embedded Grid Timeline")
|
26
40
|
|
41
|
+
#### Moment
|
27
42
|
|
28
|
-
|
43
|
+
An example of a Moment - `{% twitter https://twitter.com/i/moments/650667182356082688 maxwidth=500 %}`
|
29
44
|
|
30
|
-
|
45
|
+
![Embedded moment](https://raw.githubusercontent.com/rob-murray/jekyll-twitter-plugin/master/media/embedded-moment.png "Screenshot of embedded moment")
|
31
46
|
|
32
47
|
|
33
|
-
###
|
48
|
+
### Features
|
49
|
+
|
50
|
+
The plugin supports the following features:
|
51
|
+
|
52
|
+
* Installed via Rubygems.
|
53
|
+
* [Customisation](#customisation) - All customisation options passed to Twitter API.
|
54
|
+
* [Authentication](#authentication) - No authentication required!
|
55
|
+
* [Caching](#caching) - Twitter API responses can be cached to speed up builds.
|
56
|
+
|
57
|
+
|
58
|
+
## Getting Started
|
34
59
|
|
35
60
|
As mentioned by [Jekyll's documentation](http://jekyllrb.com/docs/plugins/#installing-a-plugin) you have two options; manually import the source file or require the plugin as a `gem`.
|
36
61
|
|
@@ -49,7 +74,6 @@ Add the `jekyll-twitter-plugin` to your site `_config.yml` file for Jekyll to im
|
|
49
74
|
gems: ['jekyll-twitter-plugin']
|
50
75
|
```
|
51
76
|
|
52
|
-
|
53
77
|
#### Manual import
|
54
78
|
|
55
79
|
> Note: this is deprecated and support will be removed in a later version.
|
@@ -63,86 +87,65 @@ $ wget https://raw.githubusercontent.com/rob-murray/jekyll-twitter-plugin/master
|
|
63
87
|
```
|
64
88
|
|
65
89
|
|
66
|
-
####
|
90
|
+
#### Plugin tag usage
|
67
91
|
|
68
|
-
|
92
|
+
To use the plugin, in your source content use the tag `twitter` and then pass additional options to the plugin, these are passed to the API.
|
69
93
|
|
70
|
-
|
94
|
+
```liquid
|
95
|
+
{% plugin_type twitter_url *options %}
|
71
96
|
|
72
|
-
|
73
|
-
|
74
|
-
twitter:
|
75
|
-
consumer_key: asdf
|
76
|
-
consumer_secret: asdf
|
77
|
-
access_token: asdf
|
78
|
-
access_token_secret: asdf
|
97
|
+
# Example for timeline of the **jekyllrb** user with a maximum of 5 Tweets and with a width of 500px
|
98
|
+
{% twitter https://twitter.com/jekyllrb maxwidth=500 limit=5 %}
|
79
99
|
```
|
80
100
|
|
81
|
-
|
82
|
-
|
101
|
+
| Argument | Required? | Description |
|
102
|
+
|---|---|---|
|
103
|
+
| `plugin_type` | Yes | Either `twitter` or `twitternocache` (same as `twitter` but does not cache api responses) |
|
104
|
+
| `twitter_url` | Yes | The Twitter URL to use, check below for supported URLs. |
|
105
|
+
| `*options` | No | Parameters for the API separated by spaces. Refer below and to respective Twitter API documentation for available parameters. |
|
83
106
|
|
84
|
-
* TWITTER_CONSUMER_KEY
|
85
|
-
* TWITTER_CONSUMER_SECRET
|
86
|
-
* TWITTER_ACCESS_TOKEN
|
87
|
-
* TWITTER_ACCESS_TOKEN_SECRET
|
88
107
|
|
89
|
-
|
90
|
-
$ export TWITTER_CONSUMER_KEY=foo etc.
|
91
|
-
# Or given a file .env with the keys and secrets
|
92
|
-
$ export $(cat .env | xargs)
|
93
|
-
```
|
108
|
+
### Supported Twitter URLs
|
94
109
|
|
110
|
+
The Twitter URLs that are supported depends on Twitter, we pass the url and all parameters to the API - check [Twitter Publish platform](https://publish.twitter.com) for availability. Here is documentation for some common types:
|
95
111
|
|
96
|
-
|
112
|
+
##### Tweet:
|
97
113
|
|
98
|
-
|
114
|
+
* [https://dev.twitter.com/web/overview](https://dev.twitter.com/web/overview)
|
115
|
+
* [https://dev.twitter.com/web/embedded-tweets/parameters](https://dev.twitter.com/web/embedded-tweets/parameters)
|
99
116
|
|
100
|
-
|
101
|
-
{% plugin_type api_type *params %}
|
102
|
-
```
|
117
|
+
##### Timeline:
|
103
118
|
|
104
|
-
|
105
|
-
|---|---|---|
|
106
|
-
| `plugin_type` | Yes | Either `twitter` or `twitternocache` (same as `twitter` but does not cache api responses) |
|
107
|
-
| `api_type` | No - defaults to **oembed** | The Twitter API to use, check below for supported APIs. |
|
108
|
-
| `*params` | Yes* | Parameters for the API separated by spaces. Refer below and to respective Twitter API documentation for available parameters. |
|
119
|
+
* [https://dev.twitter.com/web/embedded-timelines/oembed](https://dev.twitter.com/web/embedded-timelines/oembed)
|
109
120
|
|
110
|
-
|
121
|
+
##### Moments:
|
111
122
|
|
123
|
+
* [https://dev.twitter.com/web/embedded-moments/oembed](https://dev.twitter.com/web/embedded-moments/oembed)
|
112
124
|
|
113
|
-
###
|
125
|
+
### Customisation
|
114
126
|
|
115
|
-
|
127
|
+
All pairs of options and values after the URL are passed to the API, the parameters must be in pairs with the option as the key: `option=value`.
|
116
128
|
|
129
|
+
For exampled if you wanted to limit the width of the embedded content then the API supports a `maxwidth` option so you could construct the tag as below to limit it to a value of 500 (pixels).
|
117
130
|
|
118
|
-
|
131
|
+
```liquid
|
132
|
+
{% twitter https://twitter.com/jekyllrb maxwidth=500 %}
|
133
|
+
```
|
119
134
|
|
120
|
-
|
135
|
+
### Authentication
|
121
136
|
|
122
|
-
|
137
|
+
The API does not require any authentication.
|
123
138
|
|
124
|
-
|
125
|
-
# With api_type specified
|
126
|
-
{% twitter oembed status_url *options %}
|
127
|
-
# 'oembed' autoselected by default
|
128
|
-
{% twitter status_url *options %}
|
129
|
-
|
130
|
-
# Basic example
|
131
|
-
{% twitter oembed https://twitter.com/rubygems/status/518821243320287232 %}
|
132
|
-
# Oembed default example
|
133
|
-
{% twitter https://twitter.com/rubygems/status/518821243320287232 %}
|
134
|
-
# With options
|
135
|
-
{% twitter oembed https://twitter.com/rubygems/status/518821243320287232 align='right' width='350' %}
|
136
|
-
```
|
139
|
+
> Previous version of this library used an API that required API keys (TWITTER_CONSUMER_KEY TWITTER_CONSUMER_SECRET TWITTER_ACCESS_TOKEN TWITTER_ACCESS_TOKEN_SECRET). We now print a warning if these are detected as a reminder that you can safely remove them.
|
137
140
|
|
138
141
|
|
139
142
|
### Output
|
140
143
|
|
141
|
-
|
144
|
+
All content will be rendered inside a div with the class `jekyll-twitter-plugin`.
|
142
145
|
|
143
146
|
```html
|
144
|
-
<div class='
|
145
|
-
-- content --
|
147
|
+
<div class='jekyll-twitter-plugin'>
|
148
|
+
-- content from API --
|
146
149
|
</div>
|
147
150
|
```
|
148
151
|
|
@@ -150,42 +153,49 @@ If something goes wrong then a basic error message will be displayed;
|
|
150
153
|
|
151
154
|
> Tweet could not be processed
|
152
155
|
|
153
|
-
If the
|
154
|
-
|
155
|
-
> There was a '{error name}' error fetching Tweet '{Tweet status url}'
|
156
|
+
If we receive an error from the API then a message will be cached and rendered something like this below. If its a 404 then this suggests the Tweet is protected or deleted and it is not fetched again, and again. If the Tweet is restored then simply delete the cached response from `.tweet-cache` directory and build again.
|
156
157
|
|
158
|
+
```html
|
159
|
+
<div class='jekyll-twitter-plugin'>
|
160
|
+
<p>There was a '{error name}' error fetching Tweet '{Tweet status url}'</p>
|
161
|
+
</div>
|
162
|
+
```
|
157
163
|
|
158
164
|
### Caching
|
159
165
|
|
160
|
-
Twitter API responses can be cached to speed up Jekyll site builds
|
166
|
+
Twitter API responses can be cached to speed up Jekyll site builds. The reponses will be cached in a directory within your Jekyll project called `.tweet-cache`, this should not be committed to source control.
|
161
167
|
|
162
|
-
Caching is enabled by
|
168
|
+
Caching is enabled by using the `twitter` tag.
|
163
169
|
|
164
170
|
It is possible to disable caching by using the specific `twitternocache` tag.
|
165
171
|
|
166
172
|
```liquid
|
167
|
-
{% twitternocache
|
173
|
+
{% twitternocache twitter_url *options %}
|
168
174
|
|
169
175
|
# Example
|
170
|
-
{% twitternocache
|
176
|
+
{% twitternocache https://twitter.com/rubygems/status/518821243320287232 %}
|
171
177
|
|
172
178
|
```
|
173
179
|
|
174
180
|
|
175
181
|
### Contributions
|
176
182
|
|
177
|
-
I've tried hard to keep all
|
183
|
+
I've tried hard to keep all code in the one `lib/jekyll-twitter-plugin.rb` file so that people can just grab this file and include in their Jekyll `_plugins` directory if they do not want to install with Rubygems. This will be dropped in a later version.
|
178
184
|
|
179
185
|
Please use the GitHub pull-request mechanism to submit contributions.
|
180
186
|
|
181
|
-
There is a quick integration test in `spec/integration_tests.rb` that
|
187
|
+
There is a quick integration test in `spec/integration_tests.rb` that will use the **jekyll-twitter-plugin** public api and output a file `output_test.html`. Run this with the following command:
|
182
188
|
|
183
189
|
```ruby
|
184
|
-
$ ruby spec/integration_tests.rb
|
190
|
+
$ ruby spec/integration_tests.rb && open output_test.html
|
185
191
|
```
|
186
192
|
|
187
193
|
|
188
|
-
|
194
|
+
## Versioning
|
195
|
+
|
196
|
+
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/rob-murray/jekyll-twitter-plugin/tags).
|
197
|
+
|
198
|
+
|
199
|
+
## License
|
189
200
|
|
190
|
-
This project is
|
191
|
-
See LICENSE
|
201
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
|
@@ -5,13 +5,13 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "jekyll-twitter-plugin"
|
8
|
-
spec.version = "
|
8
|
+
spec.version = "2.0.0"
|
9
9
|
spec.authors = ["Rob Murray"]
|
10
10
|
spec.email = ["robmurray17@gmail.com"]
|
11
|
-
spec.summary = "A Liquid tag plugin for Jekyll that
|
11
|
+
spec.summary = "A Liquid tag plugin for Jekyll blogging engine that embeds Tweets, Timelines and more from Twitter API."
|
12
12
|
spec.homepage = "https://github.com/rob-murray/jekyll-twitter-plugin"
|
13
13
|
spec.license = "MIT"
|
14
|
-
spec.required_ruby_version = ">=
|
14
|
+
spec.required_ruby_version = ">= 2.0.0"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
@@ -19,8 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
|
20
20
|
spec.add_development_dependency "bundler", "~> 1.6"
|
21
21
|
spec.add_development_dependency "rake"
|
22
|
-
spec.add_development_dependency "rspec"
|
22
|
+
spec.add_development_dependency "rspec"
|
23
|
+
spec.add_development_dependency "webmock"
|
23
24
|
spec.add_development_dependency "byebug" if RUBY_VERSION >= "2.0"
|
24
|
-
|
25
|
-
spec.add_dependency "twitter", "~> 5.16"
|
26
25
|
end
|
@@ -1,23 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "fileutils"
|
3
|
-
require "
|
3
|
+
require "net/http"
|
4
|
+
require "uri"
|
5
|
+
require "ostruct"
|
6
|
+
require "json"
|
7
|
+
require "digest"
|
4
8
|
|
5
9
|
##
|
6
10
|
# A Liquid tag plugin for Jekyll that renders Tweets from Twitter API.
|
7
11
|
# https://github.com/rob-murray/jekyll-twitter-plugin
|
8
12
|
#
|
9
13
|
module TwitterJekyll
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
# TODO: remove after deprecation cycle
|
15
|
+
CONTEXT_API_KEYS = %w(consumer_key consumer_secret access_token access_token_secret).freeze
|
16
|
+
ENV_API_KEYS = %w(TWITTER_CONSUMER_KEY TWITTER_CONSUMER_SECRET TWITTER_ACCESS_TOKEN TWITTER_ACCESS_TOKEN_SECRET).freeze
|
17
|
+
REFER_TO_README = "Please see 'https://github.com/rob-murray/jekyll-twitter-plugin' for usage.".freeze
|
18
|
+
LIBRARY_VERSION = "jekyll-twitter-plugin-v2.0.0".freeze
|
19
|
+
REQUEST_HEADERS = { "User-Agent" => LIBRARY_VERSION }.freeze
|
20
|
+
|
21
|
+
# Cache class that writes to filesystem
|
22
|
+
# TODO: Do i really need to cache?
|
23
|
+
# @api private
|
21
24
|
class FileCache
|
22
25
|
def initialize(path)
|
23
26
|
@cache_folder = File.expand_path path
|
@@ -49,6 +52,8 @@ module TwitterJekyll
|
|
49
52
|
end
|
50
53
|
end
|
51
54
|
|
55
|
+
# Cache class that does nothing
|
56
|
+
# @api private
|
52
57
|
class NullCache
|
53
58
|
def initialize(*_args); end
|
54
59
|
|
@@ -57,117 +62,104 @@ module TwitterJekyll
|
|
57
62
|
def write(_key, _data); end
|
58
63
|
end
|
59
64
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
65
|
+
# Wrapper around an API
|
66
|
+
# @api private
|
67
|
+
class ApiClient
|
68
|
+
# Perform API request; return hash with html content
|
69
|
+
def fetch(api_request)
|
70
|
+
uri = api_request.to_uri
|
71
|
+
response = Net::HTTP.start(uri.host, use_ssl: api_request.ssl?) do |http|
|
72
|
+
http.read_timeout = 5
|
73
|
+
http.open_timeout = 5
|
74
|
+
http.get uri.request_uri, REQUEST_HEADERS
|
75
|
+
end
|
70
76
|
|
71
|
-
|
77
|
+
handle_response(api_request, response)
|
72
78
|
|
73
|
-
|
74
|
-
|
75
|
-
@status_url = params.shift
|
76
|
-
parse_args(params)
|
79
|
+
rescue Timeout::Error => e
|
80
|
+
ErrorResponse.new(api_request, e.class.name).to_h
|
77
81
|
end
|
78
82
|
|
79
|
-
def fetch; end
|
80
|
-
|
81
83
|
private
|
82
84
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
@client.status(id.to_i)
|
91
|
-
rescue *ERRORS_TO_IGNORE => e
|
92
|
-
@error = create_error(e)
|
93
|
-
return nil
|
85
|
+
def handle_response(api_request, response)
|
86
|
+
case response
|
87
|
+
when Net::HTTPSuccess
|
88
|
+
JSON.parse(response.body)
|
89
|
+
else
|
90
|
+
ErrorResponse.new(api_request, response.message).to_h
|
91
|
+
end
|
94
92
|
end
|
93
|
+
end
|
95
94
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
if k && v
|
101
|
-
v = Regexp.last_match[1] if v =~ /^'(.*)'$/
|
102
|
-
params[k] = v
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
95
|
+
# @api private
|
96
|
+
ErrorResponse = Struct.new(:request, :message) do
|
97
|
+
def html
|
98
|
+
"<p>There was a '#{message}' error fetching URL: '#{request.entity_url}'</p>"
|
106
99
|
end
|
107
100
|
|
108
|
-
def
|
109
|
-
|
101
|
+
def to_h
|
102
|
+
{ html: html }
|
110
103
|
end
|
111
104
|
end
|
112
105
|
|
113
|
-
|
114
|
-
|
106
|
+
# Holds the URI were going to request with any parameters
|
107
|
+
# @api private
|
108
|
+
ApiRequest = Struct.new(:entity_url, :params) do
|
109
|
+
TWITTER_API_URL = "https://publish.twitter.com/oembed".freeze
|
115
110
|
|
116
|
-
|
117
|
-
|
111
|
+
# Always;
|
112
|
+
def ssl?
|
113
|
+
true
|
114
|
+
end
|
118
115
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
else
|
124
|
-
error
|
116
|
+
# Return a URI for Twitter API with query params
|
117
|
+
def to_uri
|
118
|
+
URI.parse(TWITTER_API_URL).tap do |uri|
|
119
|
+
uri.query = URI.encode_www_form url_params
|
125
120
|
end
|
126
121
|
end
|
127
122
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
format("%s-%s", @status_url, @params.to_s)
|
123
|
+
# A cache key applicable to the current request with params
|
124
|
+
def cache_key
|
125
|
+
Digest::MD5.hexdigest("#{self.class.name}-#{unique_request_key}")
|
132
126
|
end
|
133
|
-
end
|
134
|
-
|
135
|
-
class ErrorResponse
|
136
|
-
attr_reader :error
|
137
127
|
|
138
|
-
|
139
|
-
@error = error
|
140
|
-
end
|
128
|
+
private
|
141
129
|
|
142
|
-
def
|
143
|
-
|
130
|
+
def url_params
|
131
|
+
params.merge(url: entity_url)
|
144
132
|
end
|
145
133
|
|
146
|
-
def
|
147
|
-
|
134
|
+
def unique_request_key
|
135
|
+
format("%s-%s", entity_url, params.to_s)
|
148
136
|
end
|
149
137
|
end
|
150
138
|
|
139
|
+
# Class to respond to Jekyll tag; entry point to library
|
140
|
+
# @api public
|
151
141
|
class TwitterTag < Liquid::Tag
|
152
|
-
ERROR_BODY_TEXT
|
153
|
-
|
142
|
+
ERROR_BODY_TEXT = "<p>Tweet could not be processed</p>".freeze
|
143
|
+
OEMBED_ARG = "oembed".freeze
|
154
144
|
|
155
145
|
attr_writer :cache # for testing
|
156
146
|
|
157
147
|
def initialize(_name, params, _tokens)
|
158
148
|
super
|
159
|
-
@
|
149
|
+
@api_request = parse_params(params)
|
160
150
|
end
|
161
151
|
|
152
|
+
# Class that implements caching strategy
|
153
|
+
# @api private
|
162
154
|
def self.cache_klass
|
163
155
|
FileCache
|
164
156
|
end
|
165
157
|
|
158
|
+
# Return html string for Jekyll engine
|
159
|
+
# @api public
|
166
160
|
def render(context)
|
167
|
-
|
168
|
-
|
169
|
-
api_client = create_api_client(@api_type, @params)
|
170
|
-
response = cached_response(api_client) || live_response(api_client)
|
161
|
+
api_secrets_deprecation_warning(context) # TODO: remove after deprecation cycle
|
162
|
+
response = cached_response || live_response
|
171
163
|
html_output_for(response)
|
172
164
|
end
|
173
165
|
|
@@ -177,84 +169,112 @@ module TwitterJekyll
|
|
177
169
|
@cache ||= self.class.cache_klass.new("./.tweet-cache")
|
178
170
|
end
|
179
171
|
|
172
|
+
def api_client
|
173
|
+
@api_client ||= ApiClient.new
|
174
|
+
end
|
175
|
+
|
176
|
+
# Return Twitter response or error html
|
177
|
+
# @api private
|
180
178
|
def html_output_for(response)
|
181
179
|
body = (response.html if response) || ERROR_BODY_TEXT
|
182
180
|
|
183
|
-
"<div class='
|
181
|
+
"<div class='jekyll-twitter-plugin'>#{body}</div>"
|
184
182
|
end
|
185
183
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
184
|
+
# Return response from API and write to cache
|
185
|
+
# @api private
|
186
|
+
def live_response
|
187
|
+
if response = api_client.fetch(@api_request)
|
188
|
+
cache.write(@api_request.cache_key, response)
|
189
|
+
build_response(response)
|
190
190
|
end
|
191
191
|
end
|
192
192
|
|
193
|
-
|
194
|
-
|
195
|
-
|
193
|
+
# Return response cache if present, otherwise nil
|
194
|
+
# @api private
|
195
|
+
def cached_response
|
196
|
+
response = cache.read(@api_request.cache_key)
|
197
|
+
build_response(response) unless response.nil?
|
196
198
|
end
|
197
199
|
|
200
|
+
# Return an `ApiRequest` with the url and arguments
|
201
|
+
# @api private
|
198
202
|
def parse_params(params)
|
199
203
|
args = params.split(/\s+/).map(&:strip)
|
204
|
+
invalid_args!(args) unless args.any?
|
200
205
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
[api_type, api_args]
|
205
|
-
when TWITTER_STATUS_URL
|
206
|
-
[DEFAULT_API_TYPE, args]
|
207
|
-
else
|
208
|
-
invalid_args!(args)
|
206
|
+
if args[0].to_s == OEMBED_ARG # TODO: remove after deprecation cycle
|
207
|
+
arguments_deprecation_warning(args)
|
208
|
+
args.shift
|
209
209
|
end
|
210
|
-
end
|
211
210
|
|
212
|
-
|
213
|
-
|
214
|
-
api_client_klass = TwitterJekyll.const_get(klass_name)
|
215
|
-
api_client_klass.new(@twitter_client, params)
|
211
|
+
url, *api_args = args
|
212
|
+
ApiRequest.new(url, parse_args(api_args))
|
216
213
|
end
|
217
214
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
215
|
+
# Transform 'a=b x=y' tag arguments into { "a" => "b", "x" => "y" }
|
216
|
+
# @api private
|
217
|
+
def parse_args(args)
|
218
|
+
args.each_with_object({}) do |arg, params|
|
219
|
+
k, v = arg.split("=").map(&:strip)
|
220
|
+
if k && v
|
221
|
+
v = Regexp.last_match[1] if v =~ /^'(.*)'$/
|
222
|
+
params[k] = v
|
223
|
+
end
|
224
224
|
end
|
225
225
|
end
|
226
226
|
|
227
|
-
|
228
|
-
|
227
|
+
# Format a response hash
|
228
|
+
# @api private
|
229
|
+
def build_response(h)
|
230
|
+
OpenStruct.new(h)
|
231
|
+
end
|
232
|
+
|
233
|
+
# TODO: remove after deprecation cycle
|
234
|
+
def arguments_deprecation_warning(args)
|
235
|
+
warn "#{LIBRARY_VERSION}: Passing '#{OEMBED_ARG}' as the first argument is not required anymore. This will result in an error in future versions.\nCalled with #{args.inspect}"
|
229
236
|
end
|
230
237
|
|
231
|
-
|
238
|
+
# TODO: remove after deprecation cycle
|
239
|
+
def api_secrets_deprecation_warning(context)
|
240
|
+
warn_if_twitter_secrets_in_context(context) || warn_if_twitter_secrets_in_env
|
241
|
+
end
|
242
|
+
|
243
|
+
# TODO: remove after deprecation cycle
|
244
|
+
def warn_if_twitter_secrets_in_context(context)
|
232
245
|
twitter_secrets = context.registers[:site].config.fetch("twitter", {})
|
233
246
|
return unless store_has_keys?(twitter_secrets, CONTEXT_API_KEYS)
|
234
247
|
|
235
|
-
|
248
|
+
warn_secrets_in_project("Jekyll _config.yml")
|
236
249
|
end
|
237
250
|
|
238
|
-
|
251
|
+
# TODO: remove after deprecation cycle
|
252
|
+
def warn_if_twitter_secrets_in_env
|
239
253
|
return unless store_has_keys?(ENV, ENV_API_KEYS)
|
240
254
|
|
241
|
-
|
255
|
+
warn_secrets_in_project("ENV")
|
242
256
|
end
|
243
257
|
|
244
|
-
|
245
|
-
|
258
|
+
# TODO: remove after deprecation cycle
|
259
|
+
def warn_secrets_in_project(source)
|
260
|
+
warn "#{LIBRARY_VERSION}: Found Twitter API keys in #{source}, this library does not require these keys anymore. You can remove these keys, if used for another library then ignore this message."
|
246
261
|
end
|
247
262
|
|
248
|
-
|
249
|
-
|
263
|
+
# TODO: remove after deprecation cycle
|
264
|
+
def store_has_keys?(store, keys)
|
265
|
+
keys.all? { |required_key| store.key?(required_key) }
|
250
266
|
end
|
251
267
|
|
268
|
+
# Raise error for invalid arguments
|
269
|
+
# @api private
|
252
270
|
def invalid_args!(arguments)
|
253
271
|
formatted_args = Array(arguments).join(" ")
|
254
272
|
raise ArgumentError, "Invalid arguments '#{formatted_args}' passed to 'jekyll-twitter-plugin'. #{REFER_TO_README}"
|
255
273
|
end
|
256
274
|
end
|
257
275
|
|
276
|
+
# Specialization of TwitterTag without any caching
|
277
|
+
# @api public
|
258
278
|
class TwitterTagNoCache < TwitterTag
|
259
279
|
def self.cache_klass
|
260
280
|
NullCache
|