jekyll-twitter-plugin 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 4c1b655f7a4220868b0d2cd098cba068f4956e68
4
- data.tar.gz: a2c010f3a7d65bd5c9fe5e2af53971fa25ca7105
2
+ SHA256:
3
+ metadata.gz: d5a28bcf4677a1a5ca462788776ead3431ed26444a2f6e2f632e1d2bc9ca5fcd
4
+ data.tar.gz: ba7ba303e24ca67c1ff1313813722c94f88353ef7d2014af2bbca7c75c53012f
5
5
  SHA512:
6
- metadata.gz: 4f83e487aac6acb23ff716b6384914d9f881abdbcddc249418bfa471702b918d5efc39700a943beae3eb1cf37513da84ef7fa2fa41f5a437df150b4e1b8be2f3
7
- data.tar.gz: 969e54b10b302987d5a8f40d64a0e545216ff587e68f517bd3b510e2f610453f0cd7afa2109842b98e53dd3825b5efab2b0747c983b2e08759c58db807779914
6
+ metadata.gz: 6fe96d45ec4959d1b98489408676f504e70398fc5aaaa4cb550201193457a7286f3a18e394298aafc68e1c2dc463b79b358d5c5c0dbef6c3b59672e6957a1e74
7
+ data.tar.gz: 2bd3d486f229a82c43ee757499fc23e975e490383b4b6d8ac725acde09818c4703b1537d47c7aaedcecb498d2963534cde5a261b37840c010b47a5c09230303e
data/.gitignore CHANGED
@@ -38,3 +38,4 @@ Gemfile.lock
38
38
  .env
39
39
  output_test.html
40
40
  .tweet-cache
41
+ test_app/
@@ -1,11 +1,13 @@
1
1
  language: ruby
2
- cache: bundler
3
2
  sudo: false
4
3
  rvm:
5
- - 2.0.0
6
- - 2.1.0
7
- - 2.2.0
8
- - 2.3.0
4
+ - 2.0
5
+ - 2.1
6
+ - 2.2
7
+ - 2.3
8
+ - 2.4
9
+ - 2.5
10
+ - 2.6
9
11
  - ruby-head
10
12
  matrix:
11
13
  allow_failures:
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  source "https://rubygems.org"
3
4
 
4
5
  # Specify your gem's dependencies in jekyll-twitter-plugin.gemspec
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # jekyll-twitter-plugin
4
4
 
5
- A Liquid tag plugin for Jekyll blogging engine that embeds Tweets, Timelines and more from Twitter API.
5
+ A Liquid tag plugin for the Jekyll blogging engine that embeds Tweets, Timelines and more from Twitter API.
6
6
 
7
7
  [![Build Status](https://travis-ci.org/rob-murray/jekyll-twitter-plugin.svg?branch=master)](https://travis-ci.org/rob-murray/jekyll-twitter-plugin)
8
8
  [![Gem Version](https://badge.fury.io/rb/jekyll-twitter-plugin.svg)](http://badge.fury.io/rb/jekyll-twitter-plugin)
@@ -12,9 +12,9 @@ A Liquid tag plugin for Jekyll blogging engine that embeds Tweets, Timelines and
12
12
 
13
13
  ## Description
14
14
 
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).
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).
16
16
 
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.
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.
18
18
 
19
19
  > You can now embed any Twitter content in your Jekyll powered blog!
20
20
 
@@ -57,7 +57,7 @@ The plugin supports the following features:
57
57
 
58
58
  ## Getting Started
59
59
 
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`.
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`.
61
61
 
62
62
 
63
63
  #### Require gem
@@ -71,7 +71,7 @@ gem 'jekyll-twitter-plugin'
71
71
  Add the `jekyll-twitter-plugin` to your site `_config.yml` file for Jekyll to import the plugin as a gem.
72
72
 
73
73
  ```ruby
74
- gems: ['jekyll-twitter-plugin']
74
+ plugins: ['jekyll-twitter-plugin']
75
75
  ```
76
76
 
77
77
  #### Manual import
@@ -89,7 +89,7 @@ $ wget https://raw.githubusercontent.com/rob-murray/jekyll-twitter-plugin/master
89
89
 
90
90
  #### Plugin tag usage
91
91
 
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.
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.
93
93
 
94
94
  ```liquid
95
95
  {% plugin_type twitter_url *options %}
@@ -98,20 +98,40 @@ To use the plugin, in your source content use the tag `twitter` and then pass ad
98
98
  {% twitter https://twitter.com/jekyllrb maxwidth=500 limit=5 %}
99
99
  ```
100
100
 
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. |
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. |
106
106
 
107
+ #### Custom variables
108
+
109
+ In addition to passing the Twitter URL directly to the plugin, you can also use [Front Matter](https://jekyllrb.com/docs/front-matter/) to store URLs as page variables. This allows you to re-use view configuration or partials by keeping the Twitter URL(s) separate to page content.
110
+
111
+ ```liquid
112
+ ---
113
+ title: My page
114
+ tweets:
115
+ - https://twitter.com/dhh/status/1162426045405921282
116
+ - https://twitter.com/rails/status/1205565185739673600
117
+ a_tweet: https://twitter.com/rubygems/status/518821243320287232
118
+ ---
119
+
120
+ {% for tweet in page.tweets %}
121
+ {% twitter tweet align=right width=350 %}
122
+ {% endfor %}
123
+
124
+ {% twitter page.a_tweet %}
125
+ ```
107
126
 
108
127
  ### Supported Twitter URLs
109
128
 
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:
129
+ The Twitter URLs that are supported depend 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:
111
130
 
112
131
  ##### Tweet:
113
132
 
114
133
  * [https://dev.twitter.com/web/overview](https://dev.twitter.com/web/overview)
134
+ * [https://dev.twitter.com/rest/reference/get/statuses/oembed](https://dev.twitter.com/rest/reference/get/statuses/oembed)
115
135
  * [https://dev.twitter.com/web/embedded-tweets/parameters](https://dev.twitter.com/web/embedded-tweets/parameters)
116
136
 
117
137
  ##### Timeline:
@@ -124,9 +144,9 @@ The Twitter URLs that are supported depends on Twitter, we pass the url and all
124
144
 
125
145
  ### Customisation
126
146
 
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`.
147
+ 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`.
128
148
 
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).
149
+ For example, if you want 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).
130
150
 
131
151
  ```liquid
132
152
  {% twitter https://twitter.com/jekyllrb maxwidth=500 %}
@@ -149,11 +169,11 @@ All content will be rendered inside a div with the class `jekyll-twitter-plugin`
149
169
  </div>
150
170
  ```
151
171
 
152
- If something goes wrong then a basic error message will be displayed;
172
+ If something goes wrong, then a basic error message will be displayed:
153
173
 
154
174
  > Tweet could not be processed
155
175
 
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.
176
+ If we receive an error from the API then a message will be cached and rendered something like this below. If it's a 404, then this suggests the Tweet is protected or deleted. I will not be fetched again and again. If the Tweet is restored, then simply delete the cached response from `.tweet-cache` directory and build again.
157
177
 
158
178
  ```html
159
179
  <div class='jekyll-twitter-plugin'>
@@ -163,7 +183,7 @@ If we receive an error from the API then a message will be cached and rendered s
163
183
 
164
184
  ### Caching
165
185
 
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.
186
+ 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.
167
187
 
168
188
  Caching is enabled by using the `twitter` tag.
169
189
 
data/Rakefile CHANGED
@@ -1,2 +1,3 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "bundler/gem_tasks"
@@ -1,11 +1,11 @@
1
- # coding: utf-8
2
1
  # frozen_string_literal: true
3
- lib = File.expand_path("../lib", __FILE__)
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $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 = "2.0.0"
8
+ spec.version = "2.1.0"
9
9
  spec.authors = ["Rob Murray"]
10
10
  spec.email = ["robmurray17@gmail.com"]
11
11
  spec.summary = "A Liquid tag plugin for Jekyll blogging engine that embeds Tweets, Timelines and more from Twitter API."
@@ -17,9 +17,10 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_development_dependency "bundler", "~> 1.6"
20
+ spec.add_development_dependency "bundler"
21
+ spec.add_development_dependency "byebug" if RUBY_VERSION >= "2.0"
21
22
  spec.add_development_dependency "rake"
22
23
  spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "rubocop"
23
25
  spec.add_development_dependency "webmock"
24
- spec.add_development_dependency "byebug" if RUBY_VERSION >= "2.0"
25
26
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "fileutils"
3
4
  require "net/http"
4
5
  require "uri"
@@ -11,12 +12,14 @@ require "digest"
11
12
  # https://github.com/rob-murray/jekyll-twitter-plugin
12
13
  #
13
14
  module TwitterJekyll
15
+ VERSION = "2.0.0".freeze
16
+ REFER_TO_README = "Please see 'https://github.com/rob-murray/jekyll-twitter-plugin' for usage.".freeze
17
+ LIBRARY_VERSION = "jekyll-twitter-plugin-v#{VERSION}".freeze
18
+ REQUEST_HEADERS = { "User-Agent" => LIBRARY_VERSION }.freeze
19
+
14
20
  # TODO: remove after deprecation cycle
15
21
  CONTEXT_API_KEYS = %w(consumer_key consumer_secret access_token access_token_secret).freeze
16
22
  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
23
 
21
24
  # Cache class that writes to filesystem
22
25
  # TODO: Do i really need to cache?
@@ -28,23 +31,22 @@ module TwitterJekyll
28
31
  end
29
32
 
30
33
  def read(key)
31
- file_to_read = cache_file(cache_filename(key))
34
+ file_to_read = cache_file(key)
32
35
  JSON.parse(File.read(file_to_read)) if File.exist?(file_to_read)
33
36
  end
34
37
 
35
38
  def write(key, data)
36
- file_to_write = cache_file(cache_filename(key))
37
- data_to_write = JSON.generate data.to_h
39
+ file_to_write = cache_file(key)
38
40
 
39
41
  File.open(file_to_write, "w") do |f|
40
- f.write(data_to_write)
42
+ f.write(JSON.generate(data.to_h))
41
43
  end
42
44
  end
43
45
 
44
46
  private
45
47
 
46
- def cache_file(filename)
47
- File.join(@cache_folder, filename)
48
+ def cache_file(key)
49
+ File.join(@cache_folder, cache_filename(key))
48
50
  end
49
51
 
50
52
  def cache_filename(cache_key)
@@ -75,7 +77,6 @@ module TwitterJekyll
75
77
  end
76
78
 
77
79
  handle_response(api_request, response)
78
-
79
80
  rescue Timeout::Error => e
80
81
  ErrorResponse.new(api_request, e.class.name).to_h
81
82
  end
@@ -142,11 +143,22 @@ module TwitterJekyll
142
143
  ERROR_BODY_TEXT = "<p>Tweet could not be processed</p>".freeze
143
144
  OEMBED_ARG = "oembed".freeze
144
145
 
146
+ URL_OR_STRING_PARAM = /^("|')?(http|https):\/\//i
147
+
145
148
  attr_writer :cache # for testing
146
149
 
147
150
  def initialize(_name, params, _tokens)
148
151
  super
149
- @api_request = parse_params(params)
152
+
153
+ # Test if first arg is a URL or starts with oembed,
154
+ # otherwise its a Jekyll variable. TODO: remove oembed after deprecation cycle
155
+ if params =~ URL_OR_STRING_PARAM || params.to_s.start_with?(OEMBED_ARG)
156
+ @fetch_from_context = false
157
+ @api_request = parse_params_from_string(params)
158
+ else
159
+ @fetch_from_context = true
160
+ @variable_params = normalize_string_params(params)
161
+ end
150
162
  end
151
163
 
152
164
  # Class that implements caching strategy
@@ -158,6 +170,12 @@ module TwitterJekyll
158
170
  # Return html string for Jekyll engine
159
171
  # @api public
160
172
  def render(context)
173
+ if fetch_from_context?
174
+ variable_name, *params = @variable_params
175
+ tweet_url = context[variable_name]
176
+ @api_request = parse_params_from_array [tweet_url, *params]
177
+ end
178
+
161
179
  api_secrets_deprecation_warning(context) # TODO: remove after deprecation cycle
162
180
  response = cached_response || live_response
163
181
  html_output_for(response)
@@ -165,6 +183,10 @@ module TwitterJekyll
165
183
 
166
184
  private
167
185
 
186
+ def fetch_from_context?
187
+ @fetch_from_context
188
+ end
189
+
168
190
  def cache
169
191
  @cache ||= self.class.cache_klass.new("./.tweet-cache")
170
192
  end
@@ -197,11 +219,19 @@ module TwitterJekyll
197
219
  build_response(response) unless response.nil?
198
220
  end
199
221
 
222
+ def parse_params_from_string(str)
223
+ args = normalize_string_params(str)
224
+ parse_params(args)
225
+ end
226
+
227
+ def parse_params_from_array(arr)
228
+ parse_params(arr)
229
+ end
230
+
200
231
  # Return an `ApiRequest` with the url and arguments
201
232
  # @api private
202
- def parse_params(params)
203
- args = params.split(/\s+/).map(&:strip)
204
- invalid_args!(args) unless args.any?
233
+ def parse_params(args)
234
+ invalid_args!(args) unless args.compact.any?
205
235
 
206
236
  if args[0].to_s == OEMBED_ARG # TODO: remove after deprecation cycle
207
237
  arguments_deprecation_warning(args)
@@ -209,25 +239,31 @@ module TwitterJekyll
209
239
  end
210
240
 
211
241
  url, *api_args = args
212
- ApiRequest.new(url, parse_args(api_args))
242
+ ApiRequest.new(url, hash_from_args(api_args))
243
+ end
244
+
245
+ # Take input arguments, remove quotes & return array of argument values
246
+ # @api private
247
+ def normalize_string_params(str)
248
+ str.to_s.gsub(/"|'/, "").split(/\s+/).map(&:strip)
213
249
  end
214
250
 
215
251
  # Transform 'a=b x=y' tag arguments into { "a" => "b", "x" => "y" }
216
252
  # @api private
217
- def parse_args(args)
218
- args.each_with_object({}) do |arg, params|
253
+ def hash_from_args(args)
254
+ args.each_with_object({}) do |arg, results|
219
255
  k, v = arg.split("=").map(&:strip)
220
256
  if k && v
221
257
  v = Regexp.last_match[1] if v =~ /^'(.*)'$/
222
- params[k] = v
258
+ results[k] = v
223
259
  end
224
260
  end
225
261
  end
226
262
 
227
263
  # Format a response hash
228
264
  # @api private
229
- def build_response(h)
230
- OpenStruct.new(h)
265
+ def build_response(response_hash)
266
+ OpenStruct.new(response_hash)
231
267
  end
232
268
 
233
269
  # TODO: remove after deprecation cycle
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  RSpec.describe TwitterJekyll::ApiRequest do
3
4
  subject(:api_request) { described_class.new(url, params) }
4
5
  let(:url) { "https://twitter.com/twitter_user/status/12345" }
@@ -23,7 +24,7 @@ RSpec.describe TwitterJekyll::ApiRequest do
23
24
  let(:params) { { align: "right" } }
24
25
 
25
26
  it "has encoded query params" do
26
- expect(URI.decode_www_form(uri.query)).to match_array [["url", url], %w(align right)]
27
+ expect(URI.decode_www_form(uri.query)).to match_array [["url", url], %w[align right]]
27
28
  end
28
29
  end
29
30
 
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  # Basic integration example - run code to produce html output
3
3
  #
4
- # * Requires .env populated with valid Twitter API creds.
5
- #
6
4
  $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
7
5
  require_relative "./support/jekyll_template"
8
6
  require "jekyll-twitter-plugin"
@@ -47,16 +45,15 @@ class TwitterRenderer
47
45
  def render
48
46
  ERB.new(template)
49
47
  .result(binding)
50
- .gsub!("src=\"//", "src=\"https://")
51
48
  end
52
49
 
53
50
  private
54
51
 
55
52
  attr_reader :options, :jekyll_context
56
53
 
57
- def render_twitter_tag(params)
58
- say_with_colour "Fetching with params: #{params}", :yellow
59
- TwitterJekyll::TwitterTag.new(nil, params, nil).render(jekyll_context)
54
+ def render_twitter_tag(option)
55
+ say_with_colour "Fetching with option: #{option}", :yellow
56
+ TwitterJekyll::TwitterTag.new(nil, option, nil).render(jekyll_context)
60
57
  end
61
58
 
62
59
  def template
@@ -76,9 +73,9 @@ class TwitterRenderer
76
73
  end
77
74
 
78
75
  def main
79
- rederer = TwitterRenderer.new(OPTIONS)
76
+ renderer = TwitterRenderer.new(OPTIONS)
80
77
  File.open(OUTPUT_FILENAME, "w") do |f|
81
- f.write rederer.render
78
+ f.write renderer.render
82
79
  end
83
80
  end
84
81
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
- $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
3
4
  require "webmock/rspec"
4
5
  require "support/jekyll_template"
5
- require "support/shared_contexts"
6
6
  require "jekyll-twitter-plugin"
7
7
  require "byebug" if RUBY_VERSION >= "2.0"
8
8
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # Hack ...or stub Liquid classes and methods used in plugin
3
4
  module Liquid
4
5
  class Tag
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  RSpec.describe TwitterJekyll::TwitterTag do
3
- let(:context) { empty_jekyll_context }
4
- let(:arguments) { "" }
5
4
  let(:api_response_hash) do
6
5
  {
7
6
  "url" => "https://twitter.com/twitter_user/status/12345",
@@ -17,17 +16,77 @@ RSpec.describe TwitterJekyll::TwitterTag do
17
16
  "version" => "1.0"
18
17
  }
19
18
  end
20
- subject { described_class.new(nil, arguments, nil) }
21
19
 
22
- describe "output from oembed request" do
20
+ shared_context "without cached response" do
21
+ let(:cache) { null_cache }
22
+
23
+ before do
24
+ subject.cache = cache
25
+ end
26
+
27
+ def null_cache
28
+ double("TwitterJekyll::NullCache", read: nil, write: nil)
29
+ end
30
+ end
31
+
32
+ shared_context "called with deprecated oembed argument url" do
33
+ # {% twitter oembed https://twitter.com/twitter_user/status/12345 option=value %}
34
+ let(:arguments) { "oembed https://twitter.com/twitter_user/status/12345" }
35
+ let(:context) { empty_jekyll_context }
36
+
37
+ subject { described_class.new(nil, arguments, nil) }
38
+ end
39
+
40
+ shared_context "called with url" do
41
+ # {% twitter https://twitter.com/twitter_user/status/12345 option=value %}
23
42
  let(:arguments) { "https://twitter.com/twitter_user/status/12345" }
43
+ let(:context) { empty_jekyll_context }
24
44
 
45
+ subject { described_class.new(nil, arguments, nil) }
46
+ end
47
+
48
+ shared_context "called with a page var" do
49
+ # {% twitter page.tweet option=value %}
50
+ let(:arguments) { "page.tweet" }
51
+ let(:context) { jekyll_context_with(arguments, params) }
52
+ let(:params) { "https://twitter.com/twitter_user/status/12345" }
53
+
54
+ subject { described_class.new(nil, arguments, nil) }
55
+ end
56
+
57
+ shared_context "called in a loop with a local var" do
58
+ # This is the same as above but ensures that same instance can render different contexts
59
+ # {% for tweet in page.tweets %}
60
+ # {% twitter tweet option=value %}
61
+ # {% endfor %}
62
+ let(:arguments) { "tweet" }
63
+ let(:context) { jekyll_context_with(arguments, params) }
64
+ let(:params) { "https://twitter.com/twitter_user/status/12345" }
65
+
66
+ subject { described_class.new(nil, arguments, nil) }
67
+ end
68
+
69
+ shared_examples "it does not allow empty arguments" do
70
+ context "without any arguments" do
71
+ let(:arguments) { "" }
72
+
73
+ it "raises an exception" do
74
+ expect_to_raise_invalid_args_error(arguments) do
75
+ subject.render(context)
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ shared_examples "it uses a cached response" do
25
82
  context "with cached response" do
26
83
  let(:cache) { double("TwitterJekyll::FileCache") }
27
84
  before do
28
85
  subject.cache = cache
29
86
  end
30
87
 
88
+ let(:arguments) { "https://twitter.com/twitter_user/status/12345" }
89
+
31
90
  it "renders response from cache" do
32
91
  expect(cache).to receive(:read).with(an_instance_of(String)).and_return(api_response_hash)
33
92
 
@@ -35,128 +94,298 @@ RSpec.describe TwitterJekyll::TwitterTag do
35
94
  expect_output_to_match_tag_content(output, api_response_hash.fetch("html"))
36
95
  end
37
96
  end
97
+ end
38
98
 
39
- context "without cached response" do
40
- let(:cache) { double("TwitterJekyll::FileCache") }
99
+ shared_examples "it handles api responses" do
100
+ context "with successful api request" do
41
101
  before do
42
- subject.cache = cache
43
- allow(cache).to receive(:read).with(an_instance_of(String)).and_return(nil)
102
+ stub_api_request(status: 200, body: api_response_hash.to_json, headers: {})
44
103
  end
45
104
 
46
- context "with successful api request" do
47
- before do
48
- stub_api_request(status: 200, body: api_response_hash.to_json, headers: {})
49
- end
105
+ it "renders response from api and writes to cache" do
106
+ expect(cache).to receive(:write).with(an_instance_of(String), api_response_hash)
50
107
 
51
- it "renders response from api and writes to cache" do
52
- expect(cache).to receive(:write).with(an_instance_of(String), api_response_hash)
108
+ output = subject.render(context)
109
+ expect_output_to_match_tag_content(output, api_response_hash.fetch("html"))
110
+ end
111
+ end
53
112
 
54
- output = subject.render(context)
55
- expect_output_to_match_tag_content(output, api_response_hash.fetch("html"))
56
- end
113
+ context "with a status not found api request" do
114
+ before do
115
+ stub_api_request(status: [404, "Not Found"], body: "", headers: {})
57
116
  end
58
117
 
59
- context "with a status not found api request" do
60
- before do
61
- stub_api_request(status: [404, "Not Found"], body: "", headers: {})
62
- end
118
+ it "renders error response and writes to cache" do
119
+ expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
63
120
 
64
- it "renders error response and writes to cache" do
65
- expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
121
+ output = subject.render(context)
122
+ expect_output_to_have_error(output, "Not Found")
123
+ end
124
+ end
66
125
 
67
- output = subject.render(context)
68
- expect_output_to_have_error(output, "Not Found")
69
- end
126
+ context "with a status request not permitted api request" do
127
+ before do
128
+ stub_api_request(status: [403, "Forbidden"], body: "", headers: {})
70
129
  end
71
130
 
72
- context "with a status request not permitted api request" do
73
- before do
74
- stub_api_request(status: [403, "Forbidden"], body: "", headers: {})
75
- end
131
+ it "renders error response and writes to cache" do
132
+ expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
76
133
 
77
- it "renders error response and writes to cache" do
78
- expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
134
+ output = subject.render(context)
135
+ expect_output_to_have_error(output, "Forbidden")
136
+ end
137
+ end
79
138
 
80
- output = subject.render(context)
81
- expect_output_to_have_error(output, "Forbidden")
82
- end
139
+ context "with a server error api request" do
140
+ before do
141
+ stub_api_request(status: [500, "Internal Server Error"], body: "", headers: {})
83
142
  end
84
143
 
85
- context "with a server error api request" do
86
- before do
87
- stub_api_request(status: [500, "Internal Server Error"], body: "", headers: {})
88
- end
144
+ it "renders error response and writes to cache" do
145
+ expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
89
146
 
90
- it "renders error response and writes to cache" do
91
- expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
147
+ output = subject.render(context)
148
+ expect_output_to_have_error(output, "Internal Server Error")
149
+ end
150
+ end
92
151
 
93
- output = subject.render(context)
94
- expect_output_to_have_error(output, "Internal Server Error")
95
- end
152
+ context "with api request that times out" do
153
+ before do
154
+ stub_api.to_timeout
96
155
  end
97
156
 
98
- context "with api request that times out" do
99
- before do
100
- stub_api.to_timeout
101
- end
157
+ it "renders error response and writes to cache" do
158
+ expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
102
159
 
103
- it "renders error response and writes to cache" do
104
- expect(cache).to receive(:write).with(an_instance_of(String), an_instance_of(Hash))
160
+ output = subject.render(context)
161
+ expect_output_to_have_error(output, "Net::OpenTimeout")
162
+ end
163
+ end
164
+ end
105
165
 
106
- output = subject.render(context)
107
- expect_output_to_have_error(output, "Timeout::Error")
108
- end
166
+ describe "output from oembed request" do
167
+ include_context "called with deprecated oembed argument url"
168
+
169
+ it_behaves_like "it does not allow empty arguments"
170
+ it_behaves_like "it uses a cached response"
171
+
172
+ context "without cached response" do
173
+ include_context "without cached response"
174
+
175
+ it "uses correct twitter url and warns of deprecation" do
176
+ api_client = api_client_double
177
+ allow(api_client).to receive(:fetch).and_return({})
178
+ allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
179
+ allow(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/12345", {}).and_call_original
180
+ allow(cache).to receive(:write)
181
+
182
+ expect do
183
+ subject = described_class.new(nil, arguments, nil)
184
+ subject.render(context)
185
+ end.to output(/Passing 'oembed' as the first argument is not required anymore/).to_stderr
186
+
187
+ expect(TwitterJekyll::ApiRequest).to have_received(:new).with("https://twitter.com/twitter_user/status/12345", {})
109
188
  end
110
189
 
111
- context "with the oembed api type as the first argument" do
112
- let(:arguments) { "oembed https://twitter.com/twitter_user/status/12345" }
113
- before do
114
- stub_api_request(status: 200, body: api_response_hash.to_json, headers: {})
190
+ context "with options" do
191
+ let(:arguments) { "oembed https://twitter.com/twitter_user/status/12345 align=right width=350" }
192
+
193
+ it "passes options to api" do
194
+ api_client = api_client_double
195
+ allow(api_client).to receive(:fetch).and_return({})
196
+ allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
197
+ allow(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/12345", "align" => "right", "width" => "350").and_call_original
198
+ allow(cache).to receive(:write)
199
+
200
+ subject = described_class.new(nil, arguments, nil)
201
+ subject.cache = cache
202
+ subject.render(context)
203
+
204
+ expect(TwitterJekyll::ApiRequest).to have_received(:new)
205
+ .with("https://twitter.com/twitter_user/status/12345", "align" => "right", "width" => "350")
115
206
  end
207
+ end
208
+
209
+ it_behaves_like "it handles api responses"
210
+ end
211
+ end
212
+
213
+ describe "output from url request" do
214
+ include_context "called with url"
215
+
216
+ it_behaves_like "it does not allow empty arguments"
217
+ it_behaves_like "it uses a cached response"
218
+
219
+ context "without cached response" do
220
+ include_context "without cached response"
221
+
222
+ it "uses correct twitter url" do
223
+ api_client = api_client_double
224
+ allow(api_client).to receive(:fetch).and_return({})
225
+ allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
226
+ allow(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/12345", {}).and_call_original
227
+ allow(cache).to receive(:write)
228
+
229
+ subject = described_class.new(nil, arguments, nil)
230
+ subject.render(context)
231
+
232
+ expect(TwitterJekyll::ApiRequest).to have_received(:new).with("https://twitter.com/twitter_user/status/12345", {})
233
+ end
234
+
235
+ context "with options" do
236
+ let(:arguments) { "https://twitter.com/twitter_user/status/12345 align=right width=350" }
237
+
238
+ it "passes options to api" do
239
+ api_client = api_client_double
240
+ allow(api_client).to receive(:fetch).and_return({})
241
+ allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
242
+ allow(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/12345", "align" => "right", "width" => "350").and_call_original
243
+ allow(cache).to receive(:write)
116
244
 
117
- it "renders response from api and writes to cache" do
118
- expect(cache).to receive(:write).with(an_instance_of(String), api_response_hash)
245
+ subject = described_class.new(nil, arguments, nil)
246
+ subject.render(context)
119
247
 
120
- output = subject.render(context)
121
- expect_output_to_match_tag_content(output, api_response_hash.fetch("html"))
248
+ expect(TwitterJekyll::ApiRequest).to have_received(:new)
249
+ .with("https://twitter.com/twitter_user/status/12345", "align" => "right", "width" => "350")
122
250
  end
123
251
  end
252
+
253
+ it_behaves_like "it handles api responses"
124
254
  end
125
255
  end
126
256
 
127
- describe "parsing arguments" do
128
- context "without any arguments" do
129
- let(:arguments) { "" }
257
+ describe "output from usage with front matter var" do
258
+ include_context "called with a page var"
130
259
 
131
- it "raises an exception" do
132
- expect_to_raise_invalid_args_error(arguments) do
133
- tag = described_class.new(nil, arguments, nil)
134
- tag.render(context)
260
+ it_behaves_like "it does not allow empty arguments" do
261
+ let(:arguments) { "page.tweet" }
262
+ let(:context) { empty_jekyll_context }
263
+ let(:params) { "" }
264
+ end
265
+
266
+ it_behaves_like "it uses a cached response"
267
+
268
+ context "without cached response" do
269
+ include_context "without cached response"
270
+
271
+ it "uses correct twitter url" do
272
+ api_client = api_client_double
273
+ allow(api_client).to receive(:fetch).and_return({})
274
+ allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
275
+ allow(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/12345", {}).and_call_original
276
+ allow(cache).to receive(:write)
277
+
278
+ subject = described_class.new(nil, arguments, nil)
279
+ subject.render(context)
280
+
281
+ expect(TwitterJekyll::ApiRequest).to have_received(:new).with("https://twitter.com/twitter_user/status/12345", {})
282
+ end
283
+
284
+ context "with options" do
285
+ let(:arguments) { "page.tweet align=left width=400" }
286
+ let(:context) { jekyll_context_with("page.tweet", params) }
287
+ let(:params) { "https://twitter.com/twitter_user/status/12345" }
288
+
289
+ it "passes options to api" do
290
+ api_client = api_client_double
291
+ allow(api_client).to receive(:fetch).and_return({})
292
+ allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
293
+ allow(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/12345", "align" => "left", "width" => "400").and_call_original
294
+ allow(cache).to receive(:write)
295
+
296
+ subject = described_class.new(nil, arguments, nil)
297
+ subject.render(context)
298
+
299
+ expect(TwitterJekyll::ApiRequest).to have_received(:new)
300
+ .with("https://twitter.com/twitter_user/status/12345", "align" => "left", "width" => "400")
135
301
  end
136
302
  end
303
+
304
+ it_behaves_like "it handles api responses"
137
305
  end
306
+ end
138
307
 
139
- context "with the oembed api type as the first argument" do
140
- let(:arguments) { "oembed https://twitter.com/twitter_user/status/12345" }
308
+ describe "output from usage in a loop with a local var" do
309
+ include_context "called in a loop with a local var"
141
310
 
142
- it "uses correct twitter url and warns of deprecation" do
311
+ it_behaves_like "it does not allow empty arguments" do
312
+ let(:arguments) { "tweet" }
313
+ let(:context) { empty_jekyll_context }
314
+ let(:params) { "" }
315
+ end
316
+
317
+ it_behaves_like "it uses a cached response"
318
+
319
+ context "without cached response" do
320
+ include_context "without cached response"
321
+
322
+ it "uses correct twitter url" do
143
323
  api_client = api_client_double
144
324
  allow(api_client).to receive(:fetch).and_return({})
145
325
  allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
146
- expect(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/12345", {}).and_call_original
326
+ allow(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/12345", {}).and_call_original
327
+ allow(cache).to receive(:write)
147
328
 
148
- expect do
149
- tag = described_class.new(nil, arguments, nil)
150
- tag.render(context)
151
- end.to output(/Passing 'oembed' as the first argument is not required anymore/).to_stderr
329
+ subject = described_class.new(nil, arguments, nil)
330
+ subject.render(context)
331
+
332
+ expect(TwitterJekyll::ApiRequest).to have_received(:new).with("https://twitter.com/twitter_user/status/12345", {})
333
+ end
334
+
335
+ it "handles many contexts passed to same instance" do
336
+ api_client = api_client_double
337
+ allow(api_client).to receive(:fetch).and_return({})
338
+ allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
339
+ allow(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/first_url", {}).and_call_original
340
+ allow(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/second_url", {}).and_call_original
341
+ allow(cache).to receive(:write)
342
+
343
+ context = double("context", registers: { site: double(config: {}) }).tap do |c|
344
+ allow(c).to receive(:[]).with(arguments).and_return(
345
+ "https://twitter.com/twitter_user/status/first_url",
346
+ "https://twitter.com/twitter_user/status/second_url"
347
+ )
348
+ end
349
+
350
+ subject = described_class.new(nil, arguments, nil)
351
+ subject.render(context)
352
+ subject.render(context)
353
+
354
+ expect(TwitterJekyll::ApiRequest).to have_received(:new).with("https://twitter.com/twitter_user/status/first_url", {})
355
+ expect(TwitterJekyll::ApiRequest).to have_received(:new).with("https://twitter.com/twitter_user/status/second_url", {})
356
+ end
357
+
358
+ context "with options" do
359
+ let(:arguments) { "tweet align=middle width=500" }
360
+ let(:context) { jekyll_context_with("tweet", params) }
361
+ let(:params) { "https://twitter.com/twitter_user/status/12345" }
362
+
363
+ it "passes options to api" do
364
+ api_client = api_client_double
365
+ allow(api_client).to receive(:fetch).and_return({})
366
+ allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
367
+ allow(TwitterJekyll::ApiRequest).to receive(:new).with("https://twitter.com/twitter_user/status/12345", "align" => "middle", "width" => "500").and_call_original
368
+ allow(cache).to receive(:write)
369
+
370
+ subject = described_class.new(nil, arguments, nil)
371
+ subject.render(context)
372
+
373
+ expect(TwitterJekyll::ApiRequest).to have_received(:new)
374
+ .with("https://twitter.com/twitter_user/status/12345", "align" => "middle", "width" => "500")
375
+ end
152
376
  end
377
+
378
+ it_behaves_like "it handles api responses"
153
379
  end
154
380
  end
155
381
 
156
382
  describe "parsing api secrets" do
383
+ include_context "called with url"
157
384
  include_context "without cached response"
158
- include_context "with a normal request and response"
159
- let(:api_client) { api_client_double }
385
+
386
+ before do
387
+ stub_api_request(status: 200, body: api_response_hash.to_json, headers: {})
388
+ end
160
389
 
161
390
  context "with api secrets provided by ENV" do
162
391
  let(:context) { double("context", registers: { site: double(config: {}) }) }
@@ -177,7 +406,7 @@ RSpec.describe TwitterJekyll::TwitterTag do
177
406
 
178
407
  context "with api secrets provided by Jekyll config" do
179
408
  let(:context) do
180
- api_secrets = %w(consumer_key consumer_secret access_token access_token_secret)
409
+ api_secrets = %w[consumer_key consumer_secret access_token access_token_secret]
181
410
  .each_with_object({}) { |secret, h| h[secret] = secret }
182
411
  double("context", registers:
183
412
  { site: double(config: { "twitter" => api_secrets }) })
@@ -220,7 +449,13 @@ RSpec.describe TwitterJekyll::TwitterTag do
220
449
  end
221
450
 
222
451
  def empty_jekyll_context
223
- double("context", registers: { site: double(config: {}) })
452
+ double("context", registers: { site: double(config: {}) }, :[] => nil)
453
+ end
454
+
455
+ def jekyll_context_with(var, params)
456
+ double("context", registers: { site: double(config: {}) }).tap do |c|
457
+ allow(c).to receive(:[]).with(var).and_return(params)
458
+ end
224
459
  end
225
460
 
226
461
  def api_client_double
@@ -237,10 +472,10 @@ RSpec.describe TwitterJekyll::TwitterTag do
237
472
  expect_output_to_match_tag_content(actual, "<p>There was a '#{error}' error fetching URL: '#{tweet_url}'</p>")
238
473
  end
239
474
 
240
- def expect_to_raise_invalid_args_error(options)
475
+ def expect_to_raise_invalid_args_error(arguments)
241
476
  raise unless block_given?
242
477
 
243
- message = "Invalid arguments '#{options}' passed to 'jekyll-twitter-plugin'. Please see 'https://github.com/rob-murray/jekyll-twitter-plugin' for usage."
478
+ message = "Invalid arguments '#{arguments}' passed to 'jekyll-twitter-plugin'. Please see 'https://github.com/rob-murray/jekyll-twitter-plugin' for usage."
244
479
  expect do
245
480
  yield
246
481
  end.to raise_error(ArgumentError, message)
metadata CHANGED
@@ -1,29 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-twitter-plugin
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Murray
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-23 00:00:00.000000000 Z
11
+ date: 2019-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.6'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.6'
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,7 +67,7 @@ dependencies:
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: webmock
70
+ name: rubocop
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
@@ -67,7 +81,7 @@ dependencies:
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
- name: byebug
84
+ name: webmock
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - ">="
@@ -105,7 +119,6 @@ files:
105
119
  - spec/integration_tests.rb
106
120
  - spec/spec_helper.rb
107
121
  - spec/support/jekyll_template.rb
108
- - spec/support/shared_contexts.rb
109
122
  - spec/twitter_tag_spec.rb
110
123
  homepage: https://github.com/rob-murray/jekyll-twitter-plugin
111
124
  licenses:
@@ -126,8 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
139
  - !ruby/object:Gem::Version
127
140
  version: '0'
128
141
  requirements: []
129
- rubyforge_project:
130
- rubygems_version: 2.5.1
142
+ rubygems_version: 3.0.3
131
143
  signing_key:
132
144
  specification_version: 4
133
145
  summary: A Liquid tag plugin for Jekyll blogging engine that embeds Tweets, Timelines
@@ -137,5 +149,4 @@ test_files:
137
149
  - spec/integration_tests.rb
138
150
  - spec/spec_helper.rb
139
151
  - spec/support/jekyll_template.rb
140
- - spec/support/shared_contexts.rb
141
152
  - spec/twitter_tag_spec.rb
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
- RSpec.shared_context "without cached response" do
3
- let(:cache) { null_cache }
4
-
5
- before do
6
- subject.cache = cache
7
- end
8
-
9
- def null_cache
10
- double("TwitterJekyll::NullCache", read: nil, write: nil)
11
- end
12
- end
13
-
14
- RSpec.shared_context "with a normal request and response" do
15
- let(:arguments) { "https://twitter.com/twitter_user/status/12345" }
16
- let(:response) { { html: "<p>tweet html</p>" } }
17
-
18
- before do
19
- allow(api_client).to receive(:fetch).and_return(response)
20
- allow(TwitterJekyll::ApiClient).to receive(:new).and_return(api_client)
21
- end
22
- end