Add initial gem content

This commit is contained in:
Rob Watson 2018-01-09 05:50:29 +00:00 committed by Rob Watson
parent 24f3d54801
commit e437a1c2e4
32 changed files with 677 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
Gemfile.lock
_site
.byebug_history

1
.rspec Normal file
View File

@ -0,0 +1 @@
--require spec_helper

5
.travis.yml Normal file
View File

@ -0,0 +1,5 @@
sudo: false
language: ruby
rvm:
- 2.5.0
before_install: gem install bundler -v 1.16.1

3
Gemfile Normal file
View File

@ -0,0 +1,3 @@
source 'https://rubygems.org'
gemspec

23
LICENSE.txt Normal file
View File

@ -0,0 +1,23 @@
(c) 2018 Rob Watson
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

77
README.md Normal file
View File

@ -0,0 +1,77 @@
## jekyll-stealthy-share
[![Build Status](https://travis-ci.org/rfwatson/jekyll-stealthy-share.svg?branch=master)](https://travis-ci.org/rfwatson/jekyll-stealthy-share)
This is a [Jekyll](https://jekyllrb.com/) plugin that adds a Liquid tag to inject share buttons into your blog.
The share buttons are HTML-only and trigger no JavaScript, so they won't track your blog's on behalf of Facebook, Twitter, Reddit or whoever else.
The injected HTML and CSS is simple and easy to customize or extend.
See it in action on https://netflux.io.
## Installation
Add `jekyll-stealthy-share` to your blog's Gemfile:
```ruby
group :jekyll_plugins do
gem 'jekyll-stealthy-share', git: 'https://github.com/rfwatson/jekyll-stealthy-share.git'
end
```
And add it to your `_config.yml`:
```yaml
plugins:
- jekyll-stealthy-share
```
## Usage
Somewhere in your layout (for example `_includes/head.html`), include the share button CSS:
```html
{% stealthy_share_assets %}
```
To inject the share buttons into your post, use this tag:
```html
{% stealthy_share_buttons %}
```
## Customizing/adding/removing buttons
To re-order or remove buttons, you can pass arguments to the liquid tag. For example:
```html
{% stealthy_share_buttons: facebook, twitter, reddit %}
```
It's also possible to add new templates of your own. If a directory `_includes/share_buttons` exists in your site's root folder, `jekyll-stealthy-share` will read templates from this location instead.
See the [`_includes` directory](https://github.com/rfwatson/jekyll-stealthy-share/tree/master/_includes) for an idea of the expected layout of each template. Additionally, you could choose to not include `{% stealthy_share_assets %}` and write your own custom CSS.
## TODO
* Add more share button options
* Make customization of buttons easier (YAML file format to define?)
* Improve default styling
* Write unit tests
## Contributions
Welcome.
## Credits
The share button SVG templates, colours and some styling are all from http://sharingbuttons.io/.
## License
MIT
## Contact
rfwatson via GitHub

6
Rakefile Normal file
View File

@ -0,0 +1,6 @@
require "bundler/gem_tasks"
require 'rspec/core/rake_task'
task default: :spec
RSpec::Core::RakeTask.new

View File

@ -0,0 +1,7 @@
<div class="share_buttons">
<h3>Share this post</h3>
<p>If you enjoyed reading this post, please consider sharing it with your network.</p>
<ul>
{{ content }}
</ul>
</div>

10
_includes/facebook.html Normal file
View File

@ -0,0 +1,10 @@
<li data-service="facebook">
<a href="https://www.facebook.com/sharer/sharer.php?u={{ url | url_param_escape }}&quote={{ title | url_param_escape }}" title="Share on Facebook" target="_blank">
<div class="button">
<div aria-hidden="true" class="icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 0C5.38 0 0 5.38 0 12s5.38 12 12 12 12-5.38 12-12S18.62 0 12 0zm3.6 11.5h-2.1v7h-3v-7h-2v-2h2V8.34c0-1.1.35-2.82 2.65-2.82h2.35v2.3h-1.4c-.25 0-.6.13-.6.66V9.5h2.34l-.24 2z"/></svg>
</div>
Facebook
</div>
</a>
</li>

View File

@ -0,0 +1,11 @@
<li data-service="hacker_news">
<a href="https://news.ycombinator.com/submitlink?u={{ url | url_param_escape }}&t={{ title || url_param_escape }}" title="Share on Hacker News" target="_blank">
<div class="button">
<div aria-hidden="true" class="icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path fill-rule="evenodd" d="M128 256c70.692 0 128-57.308 128-128C256 57.308 198.692 0 128 0 57.308 0 0 57.308 0 128c0 70.692 57.308 128 128 128zm-9.06-113.686L75 60h20.08l25.85 52.093c.397.927.86 1.888 1.39 2.883.53.994.995 2.02 1.393 3.08.265.4.463.764.596 1.095.13.334.262.63.395.898.662 1.325 1.26 2.618 1.79 3.877.53 1.26.993 2.42 1.39 3.48 1.06-2.254 2.22-4.673 3.48-7.258 1.26-2.585 2.552-5.27 3.877-8.052L161.49 60h18.69l-44.34 83.308v53.087h-16.9v-54.08z"/></svg>
</div>
Hacker News
</div>
</a>
</li>

10
_includes/reddit.html Normal file
View File

@ -0,0 +1,10 @@
<li data-service="reddit">
<a href="https://reddit.com/submit?url={{ url | url_param_escape }}&title={{ title | url_param_escape }}" title="Share on Reddit" target="_blank">
<div class="button">
<div aria-hidden="true" class="icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="9.391" cy="13.392" r=".978"/><path d="M14.057 15.814c-1.14.66-2.987.655-4.122-.004-.238-.138-.545-.058-.684.182-.13.24-.05.545.19.685.72.417 1.63.646 2.568.646.93 0 1.84-.228 2.558-.642.24-.13.32-.44.185-.68-.14-.24-.445-.32-.683-.18zM5 12.086c0 .41.23.78.568.978.27-.662.735-1.264 1.353-1.774-.2-.207-.48-.334-.79-.334-.62 0-1.13.507-1.13 1.13z"/><path d="M12 0C5.383 0 0 5.383 0 12s5.383 12 12 12 12-5.383 12-12S18.617 0 12 0zm6.673 14.055c.01.104.022.208.022.314 0 2.61-3.004 4.73-6.695 4.73s-6.695-2.126-6.695-4.74c0-.105.013-.21.022-.313C4.537 13.73 4 12.97 4 12.08c0-1.173.956-2.13 2.13-2.13.63 0 1.218.29 1.618.757 1.04-.607 2.345-.99 3.77-1.063.057-.803.308-2.33 1.388-2.95.633-.366 1.417-.323 2.322.085.302-.81 1.076-1.397 1.99-1.397 1.174 0 2.13.96 2.13 2.13 0 1.177-.956 2.133-2.13 2.133-1.065 0-1.942-.79-2.098-1.81-.734-.4-1.315-.506-1.716-.276-.6.346-.818 1.395-.88 2.087 1.407.08 2.697.46 3.728 1.065.4-.468.987-.756 1.617-.756 1.17 0 2.13.953 2.13 2.13 0 .89-.54 1.65-1.33 1.97z"/><circle cx="14.609" cy="13.391" r=".978"/><path d="M17.87 10.956c-.302 0-.583.128-.79.334.616.51 1.082 1.112 1.352 1.774.34-.197.568-.566.568-.978 0-.623-.507-1.13-1.13-1.13z"/></svg>
</div>
Reddit
</div>
</a>
</li>

10
_includes/twitter.html Normal file
View File

@ -0,0 +1,10 @@
<li data-service="twitter">
<a href="https://twitter.com/share?url={{ url | url_param_escape }}" title="Share on Twitter" target="_blank">
<div class="button">
<div aria-hidden="true" class="icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 0C5.38 0 0 5.38 0 12s5.38 12 12 12 12-5.38 12-12S18.62 0 12 0zm5.26 9.38v.34c0 3.48-2.64 7.5-7.48 7.5-1.48 0-2.87-.44-4.03-1.2 1.37.17 2.77-.2 3.9-1.08-1.16-.02-2.13-.78-2.46-1.83.38.1.8.07 1.17-.03-1.2-.24-2.1-1.3-2.1-2.58v-.05c.35.2.75.32 1.18.33-.7-.47-1.17-1.28-1.17-2.2 0-.47.13-.92.36-1.3C7.94 8.85 9.88 9.9 12.06 10c-.04-.2-.06-.4-.06-.6 0-1.46 1.18-2.63 2.63-2.63.76 0 1.44.3 1.92.82.6-.12 1.95-.27 1.95-.27-.35.53-.72 1.66-1.24 2.04z"/></svg>
</div>
Twitter
</div>
</a>
</li>

93
assets/share.css Normal file
View File

@ -0,0 +1,93 @@
.share_buttons {
margin: 30px auto;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
padding: 15px 0px;
}
.share_buttons > ul {
margin-left: 0px;
margin-bottom: 0px;
}
.share_buttons > ul > li {
list-style-type: none;
display: inline-block;
}
.share_buttons > ul > li > a, .icon {
display: inline-block;
}
.share_buttons > ul > li > a {
text-decoration: none;
color: #fff;
margin: 0.5em 0px;
line-height: 1.5em;
}
.share_buttons > ul > li > a > .button {
border-radius: 5px;
transition: 25ms ease-out;
padding: 0.5em 0.75em;
font-family: Helvetica Neue,Helvetica,Arial,sans-serif;
text-align: center;
min-width: 120px;
}
.share_buttons > ul > li > a > .button > .icon {
fill: #fff;
stroke: none;
position: relative;
top: -1px;
left: 1px;
}
.share_buttons > ul > li > a > .button > .icon > svg {
width: 1em;
height: 1em;
margin-right: 0.4em;
line-height: 1em;
}
.share_buttons > ul > li > a > .button > .icon > svg > path {
fill: #fff;
}
.share_buttons > ul > li[data-service="facebook"] > a > .button {
background-color: #3b5998;
border-color: #3b5998;
}
.share_buttons > ul > li[data-service="facebook"] > a:active > .button,
.share_buttons > ul > li[data-service="facebook"] > a:hover > .button {
background-color: #2d4373;
border-color: #2d4373;
}
.share_buttons > ul > li[data-service="twitter"] > a > .button {
background-color: #55acee;
}
.share_buttons > ul > li[data-service="twitter"] > a:active > .button,
.share_buttons > ul > li[data-service="twitter"] > a:hover > .button {
background-color: #2795e9;
}
.share_buttons > ul > li[data-service="reddit"] > a > .button {
background-color: #5f99cf;
}
.share_buttons > ul > li[data-service="reddit"] > a:active > .button,
.share_buttons > ul > li[data-service="reddit"] > a:hover > .button {
background-color: #3a80c1;
}
.share_buttons > ul > li[data-service="hacker_news"] > a > .button {
background-color: #ff6600;
}
.share_buttons > ul > li[data-service="hacker_news"] > a:active > .button,
.share_buttons > ul > li[data-service="hacker_news"] > a:hover > .button {
background-color: #fb6200;
}

View File

@ -0,0 +1,27 @@
# encoding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'jekyll/stealthy_share/version'
Gem::Specification.new do |spec|
spec.name = "jekyll-stealthy-share"
spec.version = Jekyll::StealthyShare::VERSION
spec.authors = ["Rob Watson"]
spec.email = ["hello@netflux.io"]
spec.description = %q{Privacy-conscious share buttons for Jekyll}
spec.summary = spec.description
spec.homepage = "https://github.com/rfwatson"
spec.license = "MIT"
spec.files = `git ls-files`.split($/)
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_development_dependency "bundler", "~> 1.16"
spec.add_development_dependency "rake"
spec.add_development_dependency "jekyll"
spec.add_development_dependency "rspec"
spec.add_development_dependency 'capybara'
spec.add_development_dependency 'byebug'
end

View File

@ -0,0 +1,5 @@
require 'jekyll'
require 'jekyll/stealthy_share'
Liquid::Template.register_tag('stealthy_share_buttons', Jekyll::StealthyShare::Tag)
Liquid::Template.register_tag('stealthy_share_assets', Jekyll::StealthyShare::AssetTag)

View File

@ -0,0 +1,33 @@
require 'jekyll/stealthy_share/version'
require 'jekyll/stealthy_share/template'
require 'jekyll/stealthy_share/tag'
require 'jekyll/stealthy_share/asset_tag'
require 'jekyll/stealthy_share/generator'
module Jekyll
module StealthyShare
class << self
attr_accessor :site
def base_path
File.join(File.dirname(__FILE__), '../..')
end
def templates_path
return source_templates_path if File.directory?(source_templates_path)
default_templates_path
end
private
def default_templates_path
File.join(base_path, '_includes')
end
def source_templates_path
File.join(site.source, '_includes', 'share_buttons')
end
end
end
end

View File

@ -0,0 +1,9 @@
module Jekyll
module StealthyShare
class AssetTag < Liquid::Tag
def render(context)
%Q{<link rel="stylesheet" href="/assets/share.css">}
end
end
end
end

View File

@ -0,0 +1,18 @@
require 'jekyll/generator'
module Jekyll
module StealthyShare
class Generator < Jekyll::Generator
def generate(site)
StealthyShare.site = site
site.static_files << StaticFile.new(
site,
StealthyShare.base_path,
'assets',
'share.css'
)
end
end
end
end

View File

@ -0,0 +1,61 @@
require 'liquid'
module Jekyll
module StealthyShare
class Tag < Liquid::Tag
HTML = '.html'.freeze
def initialize(tag_name, text, tokens)
@text = text
end
def render(context)
permalink = page(context)['url']
title = page(context)['title']
url = URI.join(base_url(context), permalink)
buttons = templates.map do |template|
liquid_template = Liquid::Template.parse(template)
liquid_template.render(
'url' => url.to_s,
'title' => title.to_s
)
end
container = Template.read('_container.html').first
render_template(container, 'content' => buttons.join)
end
private
def render_template(template, data)
t = Liquid::Template.parse(template)
t.render(data)
end
def templates
basenames = @text.scan(/\w+/)
basenames = Template.basenames if basenames.empty?
basenames.map! do |basename|
basename.end_with?(HTML) ? basename : basename + HTML
end
missing = basenames - Template.basenames
if missing.any?
raise "Unknown share button templates: #{missing.inspect}."
end
Template.read(*basenames)
end
def page(context)
context.environments.first.page
end
def base_url(context)
context.registers[:site].config['url']
end
end
end
end

View File

@ -0,0 +1,26 @@
module Jekyll
module StealthyShare
class Template
class << self
extend Forwardable
def_delegator StealthyShare, :templates_path
def basenames
all.map(&File.public_method(:basename)).sort
end
def read(*basenames)
basenames.map do |basename|
File.read(File.join(templates_path, basename))
end
end
private
def all
Dir.glob(File.join(templates_path, '[!_]*.html'))
end
end
end
end
end

View File

@ -0,0 +1,5 @@
module Jekyll
module StealthyShare
VERSION = '0.1.0'
end
end

109
spec/spec_helper.rb Normal file
View File

@ -0,0 +1,109 @@
ENV['JEKYLL_LOG_LEVEL'] = 'warn'
require 'jekyll'
require 'byebug'
require 'capybara/rspec'
require_relative 'support/helpers'
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# The generated `.rspec` file contains `--require spec_helper` which will cause
# this file to always be loaded, without a need to explicitly require it in any
# files.
#
# Given that it is always loaded, you are encouraged to keep this file as
# light-weight as possible. Requiring heavyweight dependencies from this file
# will add to the boot time of your test suite on EVERY test run, even for an
# individual file that may not need all of that loaded. Instead, consider making
# a separate helper file that requires the additional dependencies and performs
# the additional setup, and require it from the spec files that actually need
# it.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
# This option will default to `true` in RSpec 4. It makes the `description`
# and `failure_message` of custom matchers include text for helper methods
# defined using `chain`, e.g.:
# be_bigger_than(2).and_smaller_than(4).description
# # => "be bigger than 2 and smaller than 4"
# ...rather than:
# # => "be bigger than 2"
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
# rspec-mocks config goes here. You can use an alternate test double
# library (such as bogus or mocha) by changing the `mock_with` option here.
config.mock_with :rspec do |mocks|
# Prevents you from mocking or stubbing a method that does not exist on
# a real object. This is generally recommended, and will default to
# `true` in RSpec 4.
mocks.verify_partial_doubles = true
end
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
# have no way to turn it off -- the option exists only for backwards
# compatibility in RSpec 3). It causes shared context metadata to be
# inherited by the metadata hash of host groups and examples, rather than
# triggering implicit auto-inclusion in groups with matching metadata.
config.shared_context_metadata_behavior = :apply_to_host_groups
# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
=begin
# This allows you to limit a spec run to individual examples or groups
# you care about by tagging them with `:focus` metadata. When nothing
# is tagged with `:focus`, all examples get run. RSpec also provides
# aliases for `it`, `describe`, and `context` that include `:focus`
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
config.filter_run_when_matching :focus
# Allows RSpec to persist some state between runs in order to support
# the `--only-failures` and `--next-failure` CLI options. We recommend
# you configure your source control system to ignore this file.
config.example_status_persistence_file_path = "spec/examples.txt"
# Limits the available syntax to the non-monkey patched syntax that is
# recommended. For more details, see:
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
config.disable_monkey_patching!
# This setting enables warnings. It's recommended, but in some cases may
# be too noisy due to issues in dependencies.
config.warnings = true
# Many RSpec users commonly either run the entire suite or an individual
# file, and it's useful to allow more verbose output when running an
# individual spec file.
if config.files_to_run.one?
# Use the documentation formatter for detailed output,
# unless a formatter has already been configured
# (e.g. via a command-line flag).
config.default_formatter = "doc"
end
# Print the 10 slowest examples and example groups at the
# end of the spec run, to help surface which specs are running
# particularly slow.
config.profile_examples = 10
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = :random
# Seed global randomization in this process using the `--seed` CLI option.
# Setting this allows you to use `--seed` to deterministically reproduce
# test failures related to randomization by passing the same `--seed` value
# as the one that triggered the failure.
Kernel.srand config.seed
=end
config.include Helpers
end

View File

@ -0,0 +1,60 @@
require 'jekyll/stealthy_share'
RSpec.describe Jekyll::StealthyShare, type: :feature do
subject { page }
context 'on a basic site' do
include_context 'basic site'
context 'a page with share buttons injected' do
before do
visit '/2018/01/01/buttons.html'
end
it { is_expected.to have_css('.share_buttons') }
it { is_expected.to have_content('Share this post') }
it 'injects all buttons' do
count = page.all('.share_buttons li').size
expect(count).to eq 4
end
end
context 'a page with no share buttons injected' do
before do
visit '/2018/01/02/no-buttons.html'
end
it { is_expected.not_to have_css('.share_buttons') }
it { is_expected.not_to have_content('Share this:') }
end
end
context 'a site with overrides' do
include_context 'a site with overrides'
context 'a page with share buttons injected' do
before do
visit '/2018/01/01/buttons.html'
end
it { is_expected.to have_css('.share_buttons') }
it { is_expected.to have_content('Share this:') }
it { is_expected.to have_content('Test button') }
it 'injects all buttons' do
count = page.all('.share_buttons li').size
expect(count).to eq 1
end
end
context 'a page with no share buttons injected' do
before do
visit '/2018/01/02/no-buttons.html'
end
it { is_expected.not_to have_css('.share_buttons') }
it { is_expected.not_to have_content('Share this:') }
end
end
end

View File

View File

@ -0,0 +1,7 @@
---
title: hello
---
Hello world
{% stealthy_share_buttons %}

View File

@ -0,0 +1,5 @@
---
title: hello
---
Hello world

View File

@ -0,0 +1,6 @@
<div class="share_buttons">
<h3>Share this:</h3>
<ul>
{{ content }}
</ul>
</div>

View File

@ -0,0 +1 @@
<li>Test button</li>

View File

@ -0,0 +1,7 @@
---
title: hello
---
Hello world
{% stealthy_share_buttons %}

View File

@ -0,0 +1,5 @@
---
title: hello
---
Hello world

34
spec/support/helpers.rb Normal file
View File

@ -0,0 +1,34 @@
module Helpers
shared_context 'shared' do
let(:config) do
Jekyll.configuration(
'source' => source,
'destination' => File.expand_path('_site'),
'url' => 'http://www.example.com',
'name' => 'Test site',
'plugins' => ['jekyll-stealthy-share']
)
end
let(:site) do
Jekyll::Site.new(config)
end
before do
site.process
Capybara.app = Rack::File.new(site.dest)
end
end
shared_context 'basic site' do
include_context 'shared'
let(:source) { File.expand_path('spec/support/fixtures/basic') }
end
shared_context 'a site with overrides' do
include_context 'shared'
let(:source) { File.expand_path('spec/support/fixtures/overrides') }
end
end