Skip to content

Commit

Permalink
Support Jeykyll page variables (#27)
Browse files Browse the repository at this point in the history
* Support Jeykyll page variables (wip)

* wip

* Fix specs

* Any 2.n.x version

* tryn to get it workin

* wip; updating tests

* Finish tests

* Tidy

* Version 2.1.0

* Readme update

* update text
  • Loading branch information
rob-murray committed Dec 19, 2019
1 parent 0d9ae70 commit 9641cf0
Show file tree
Hide file tree
Showing 13 changed files with 417 additions and 147 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -38,3 +38,4 @@ Gemfile.lock
.env
output_test.html
.tweet-cache
test_app/
14 changes: 7 additions & 7 deletions .travis.yml
@@ -1,13 +1,13 @@
language: ruby
sudo: false
rvm:
- 2.0.0
- 2.1.0
- 2.2.0
- 2.3.0
- 2.4.0
- 2.5.0
- 2.6.0
- 2.0
- 2.1
- 2.2
- 2.3
- 2.4
- 2.5
- 2.6
- ruby-head
matrix:
allow_failures:
Expand Down
1 change: 1 addition & 0 deletions Gemfile
@@ -1,4 +1,5 @@
# frozen_string_literal: true

source "https://rubygems.org"

# Specify your gem's dependencies in jekyll-twitter-plugin.gemspec
Expand Down
29 changes: 24 additions & 5 deletions README.md
Expand Up @@ -98,12 +98,31 @@ To use the plugin, in your source content use the tag `twitter` and then pass ad
{% twitter https://twitter.com/jekyllrb maxwidth=500 limit=5 %}
```

| Argument | Required? | Description |
|---|---|---|
| `plugin_type` | Yes | Either `twitter` or `twitternocache` (same as `twitter` but does not cache api responses) |
| `twitter_url` | Yes | The Twitter URL to use, check below for supported URLs. |
| `*options` | No | Parameters for the API separated by spaces. Refer below and to respective Twitter API documentation for available parameters. |
| Argument | Required? | Description |
| --- | --- | --- |
| `plugin_type` | Yes | Either `twitter` or `twitternocache` (same as `twitter` but does not cache api responses) |
| `twitter_url` | Yes | The Twitter URL to use, check below for supported URLs. |
| `*options` | No | Parameters for the API separated by spaces. Refer below and to respective Twitter API documentation for available parameters. |

#### Custom variables

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.

```liquid
---
title: My page
tweets:
- https://twitter.com/dhh/status/1162426045405921282
- https://twitter.com/rails/status/1205565185739673600
a_tweet: https://twitter.com/rubygems/status/518821243320287232
---
{% for tweet in page.tweets %}
{% twitter tweet align=right width=350 %}
{% endfor %}
{% twitter page.a_tweet %}
```

### Supported Twitter URLs

Expand Down
1 change: 1 addition & 0 deletions Rakefile
@@ -1,2 +1,3 @@
# frozen_string_literal: true

require "bundler/gem_tasks"
9 changes: 5 additions & 4 deletions jekyll-twitter-plugin.gemspec
@@ -1,11 +1,11 @@
# coding: utf-8
# frozen_string_literal: true
lib = File.expand_path("../lib", __FILE__)

lib = File.expand_path("lib", __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)

Gem::Specification.new do |spec|
spec.name = "jekyll-twitter-plugin"
spec.version = "2.0.0"
spec.version = "2.1.0"
spec.authors = ["Rob Murray"]
spec.email = ["robmurray17@gmail.com"]
spec.summary = "A Liquid tag plugin for Jekyll blogging engine that embeds Tweets, Timelines and more from Twitter API."
Expand All @@ -18,8 +18,9 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]

spec.add_development_dependency "bundler"
spec.add_development_dependency "byebug" if RUBY_VERSION >= "2.0"
spec.add_development_dependency "rake"
spec.add_development_dependency "rspec"
spec.add_development_dependency "rubocop"
spec.add_development_dependency "webmock"
spec.add_development_dependency "byebug" if RUBY_VERSION >= "2.0"
end
68 changes: 51 additions & 17 deletions lib/jekyll-twitter-plugin.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true

require "fileutils"
require "net/http"
require "uri"
Expand Down Expand Up @@ -30,23 +31,22 @@ def initialize(path)
end

def read(key)
file_to_read = cache_file(cache_filename(key))
file_to_read = cache_file(key)
JSON.parse(File.read(file_to_read)) if File.exist?(file_to_read)
end

def write(key, data)
file_to_write = cache_file(cache_filename(key))
data_to_write = JSON.generate data.to_h
file_to_write = cache_file(key)

File.open(file_to_write, "w") do |f|
f.write(data_to_write)
f.write(JSON.generate(data.to_h))
end
end

private

def cache_file(filename)
File.join(@cache_folder, filename)
def cache_file(key)
File.join(@cache_folder, cache_filename(key))
end

def cache_filename(cache_key)
Expand Down Expand Up @@ -77,7 +77,6 @@ def fetch(api_request)
end

handle_response(api_request, response)

rescue Timeout::Error => e
ErrorResponse.new(api_request, e.class.name).to_h
end
Expand Down Expand Up @@ -144,11 +143,22 @@ class TwitterTag < Liquid::Tag
ERROR_BODY_TEXT = "<p>Tweet could not be processed</p>".freeze
OEMBED_ARG = "oembed".freeze

URL_OR_STRING_PARAM = /^("|')?(http|https):\/\//i

attr_writer :cache # for testing

def initialize(_name, params, _tokens)
super
@api_request = parse_params(params)

# Test if first arg is a URL or starts with oembed,
# otherwise its a Jekyll variable. TODO: remove oembed after deprecation cycle
if params =~ URL_OR_STRING_PARAM || params.to_s.start_with?(OEMBED_ARG)
@fetch_from_context = false
@api_request = parse_params_from_string(params)
else
@fetch_from_context = true
@variable_params = normalize_string_params(params)
end
end

# Class that implements caching strategy
Expand All @@ -160,13 +170,23 @@ def self.cache_klass
# Return html string for Jekyll engine
# @api public
def render(context)
if fetch_from_context?
variable_name, *params = @variable_params
tweet_url = context[variable_name]
@api_request = parse_params_from_array [tweet_url, *params]
end

api_secrets_deprecation_warning(context) # TODO: remove after deprecation cycle
response = cached_response || live_response
html_output_for(response)
end

private

def fetch_from_context?
@fetch_from_context
end

def cache
@cache ||= self.class.cache_klass.new("./.tweet-cache")
end
Expand Down Expand Up @@ -199,37 +219,51 @@ def cached_response
build_response(response) unless response.nil?
end

def parse_params_from_string(str)
args = normalize_string_params(str)
parse_params(args)
end

def parse_params_from_array(arr)
parse_params(arr)
end

# Return an `ApiRequest` with the url and arguments
# @api private
def parse_params(params)
args = params.split(/\s+/).map(&:strip)
invalid_args!(args) unless args.any?
def parse_params(args)
invalid_args!(args) unless args.compact.any?

if args[0].to_s == OEMBED_ARG # TODO: remove after deprecation cycle
arguments_deprecation_warning(args)
args.shift
end

url, *api_args = args
ApiRequest.new(url, parse_args(api_args))
ApiRequest.new(url, hash_from_args(api_args))
end

# Take input arguments, remove quotes & return array of argument values
# @api private
def normalize_string_params(str)
str.to_s.gsub(/"|'/, "").split(/\s+/).map(&:strip)
end

# Transform 'a=b x=y' tag arguments into { "a" => "b", "x" => "y" }
# @api private
def parse_args(args)
args.each_with_object({}) do |arg, params|
def hash_from_args(args)
args.each_with_object({}) do |arg, results|
k, v = arg.split("=").map(&:strip)
if k && v
v = Regexp.last_match[1] if v =~ /^'(.*)'$/
params[k] = v
results[k] = v
end
end
end

# Format a response hash
# @api private
def build_response(h)
OpenStruct.new(h)
def build_response(response_hash)
OpenStruct.new(response_hash)
end

# TODO: remove after deprecation cycle
Expand Down
3 changes: 2 additions & 1 deletion spec/api_request_spec.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true

RSpec.describe TwitterJekyll::ApiRequest do
subject(:api_request) { described_class.new(url, params) }
let(:url) { "https://twitter.com/twitter_user/status/12345" }
Expand All @@ -23,7 +24,7 @@
let(:params) { { align: "right" } }

it "has encoded query params" do
expect(URI.decode_www_form(uri.query)).to match_array [["url", url], %w(align right)]
expect(URI.decode_www_form(uri.query)).to match_array [["url", url], %w[align right]]
end
end

Expand Down
12 changes: 5 additions & 7 deletions spec/integration_tests.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
# Basic integration example - run code to produce html output
#
# * Requires .env populated with valid Twitter API creds.
#
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
require_relative "./support/jekyll_template"
require "jekyll-twitter-plugin"
Expand Down Expand Up @@ -53,9 +51,9 @@ def render

attr_reader :options, :jekyll_context

def render_twitter_tag(params)
say_with_colour "Fetching with params: #{params}", :yellow
TwitterJekyll::TwitterTag.new(nil, params, nil).render(jekyll_context)
def render_twitter_tag(option)
say_with_colour "Fetching with option: #{option}", :yellow
TwitterJekyll::TwitterTag.new(nil, option, nil).render(jekyll_context)
end

def template
Expand All @@ -75,9 +73,9 @@ def template
end

def main
rederer = TwitterRenderer.new(OPTIONS)
renderer = TwitterRenderer.new(OPTIONS)
File.open(OUTPUT_FILENAME, "w") do |f|
f.write rederer.render
f.write renderer.render
end
end

Expand Down
4 changes: 2 additions & 2 deletions spec/spec_helper.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)

$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
require "webmock/rspec"
require "support/jekyll_template"
require "support/shared_contexts"
require "jekyll-twitter-plugin"
require "byebug" if RUBY_VERSION >= "2.0"

Expand Down
1 change: 1 addition & 0 deletions spec/support/jekyll_template.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true

# Hack ...or stub Liquid classes and methods used in plugin
module Liquid
class Tag
Expand Down
22 changes: 0 additions & 22 deletions spec/support/shared_contexts.rb

This file was deleted.

0 comments on commit 9641cf0

Please sign in to comment.