dropcaster 0.0.6 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +5 -5
  2. data/.gemnasium.yml +1 -0
  3. data/.gitignore +12 -14
  4. data/.rubocop.yml +34 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +20 -5
  7. data/CONTRIBUTING.md +18 -0
  8. data/Dockerfile +4 -0
  9. data/Gemfile +2 -0
  10. data/Gemfile.lock +309 -65
  11. data/Guardfile +8 -6
  12. data/LICENSE.txt +1 -1
  13. data/README.markdown +43 -37
  14. data/Rakefile +46 -2
  15. data/{TODO → TODO.markdown} +0 -0
  16. data/VISION.markdown +52 -18
  17. data/Vagrantfile +10 -0
  18. data/bin/dropcaster +57 -53
  19. data/bin/lstags +29 -28
  20. data/dropcaster.gemspec +31 -20
  21. data/lib/dropcaster.rb +2 -15
  22. data/lib/dropcaster/channel.rb +54 -43
  23. data/lib/dropcaster/channel_file_locator.rb +8 -5
  24. data/lib/dropcaster/contributors.rb +39 -0
  25. data/lib/dropcaster/errors.rb +6 -4
  26. data/lib/dropcaster/item.rb +20 -14
  27. data/lib/dropcaster/log_formatter.rb +10 -0
  28. data/lib/dropcaster/logging.rb +13 -0
  29. data/lib/dropcaster/version.rb +3 -1
  30. data/templates/channel.html.erb +3 -6
  31. data/templates/channel.rss.erb +6 -5
  32. data/test/Vagrantfile +3 -1
  33. data/test/bin/vagrant-status +37 -31
  34. data/test/extensions/windows.rb +3 -1
  35. data/test/fixtures/extension.MP3 +0 -0
  36. data/test/fixtures/special &.mp3 +0 -0
  37. data/test/fixtures/test_template.json.erb +3 -4
  38. data/test/helper.rb +4 -1
  39. data/test/unit/test_app.rb +29 -24
  40. data/test/unit/test_channel.rb +9 -5
  41. data/test/unit/test_channel_locator.rb +8 -5
  42. data/test/unit/test_channel_xml.rb +29 -9
  43. data/test/unit/{test_item.rb → test_itunes_item.rb} +5 -6
  44. data/website/.gitignore +2 -0
  45. data/website/README.markdown +8 -0
  46. data/website/_config.yml +14 -0
  47. data/website/_front_matter/contributing.yaml +5 -0
  48. data/website/_front_matter/index.yaml +3 -0
  49. data/website/_front_matter/vision.yaml +5 -0
  50. data/website/_includes/footer.html +55 -0
  51. data/website/_includes/head.html +11 -0
  52. data/website/_includes/header.html +27 -0
  53. data/website/_layouts/default.html +20 -0
  54. data/website/_layouts/page.html +14 -0
  55. data/website/_layouts/post.html +15 -0
  56. data/website/_sass/_base.scss +204 -0
  57. data/website/_sass/_layout.scss +236 -0
  58. data/website/_sass/_syntax-highlighting.scss +67 -0
  59. data/website/css/main.scss +49 -0
  60. data/website/deploy.sh +23 -0
  61. data/website/feed.xml +30 -0
  62. metadata +150 -23
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
 
3
5
  class TestChannel < MiniTest::Test
@@ -9,7 +11,7 @@ class TestChannel < MiniTest::Test
9
11
  end
10
12
 
11
13
  def test_item_count
12
- assert_equal(1, @channel.items.size)
14
+ assert_equal(NUMBER_OF_MP3_FILES, @channel.items.size)
13
15
  end
14
16
 
15
17
  def test_channel
@@ -47,7 +49,9 @@ class TestChannel < MiniTest::Test
47
49
  end
48
50
 
49
51
  def test_channel_explicit_nil
50
- assert_channel_explicit(nil, nil)
52
+ @options[:explicit] = nil
53
+ channel = Dropcaster::Channel.new(FIXTURES_DIR, @options)
54
+ assert_nil(channel.explicit)
51
55
  end
52
56
 
53
57
  def test_channel_explicit_clean
@@ -62,19 +66,19 @@ class TestChannel < MiniTest::Test
62
66
 
63
67
  def test_raise_on_missing_title
64
68
  assert_raises Dropcaster::MissingAttributeError do
65
- Dropcaster::Channel.new(FIXTURES_DIR, {:url => 'bar', :description => 'foobar'})
69
+ Dropcaster::Channel.new(FIXTURES_DIR, { url: 'bar', description: 'foobar' })
66
70
  end
67
71
  end
68
72
 
69
73
  def test_raise_on_missing_url
70
74
  assert_raises Dropcaster::MissingAttributeError do
71
- Dropcaster::Channel.new(FIXTURES_DIR, {:title => 'foo', :description => 'foobar'})
75
+ Dropcaster::Channel.new(FIXTURES_DIR, { title: 'foo', description: 'foobar' })
72
76
  end
73
77
  end
74
78
 
75
79
  def test_raise_on_missing_description
76
80
  assert_raises Dropcaster::MissingAttributeError do
77
- Dropcaster::Channel.new(FIXTURES_DIR, {:title => 'foo', :url => 'bar'})
81
+ Dropcaster::Channel.new(FIXTURES_DIR, { title: 'foo', url: 'bar' })
78
82
  end
79
83
  end
80
84
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
  require 'tmpdir'
3
5
  require 'pry'
@@ -8,6 +10,7 @@ class TestChannelLocator < MiniTest::Test
8
10
  def setup
9
11
  @temp_dir = Dir.mktmpdir
10
12
  end
13
+
11
14
  def teardown
12
15
  FileUtils.remove_entry_secure(@temp_dir)
13
16
  end
@@ -30,7 +33,7 @@ class TestChannelLocator < MiniTest::Test
30
33
  end
31
34
 
32
35
  def test_array_of_files_same_dir
33
- sources = Array.new
36
+ sources = []
34
37
  sources << File.join(@temp_dir, 'file1.mp3')
35
38
  sources << File.join(@temp_dir, 'file2.mp3')
36
39
  sources << File.join(@temp_dir, 'file3.mp3')
@@ -39,7 +42,7 @@ class TestChannelLocator < MiniTest::Test
39
42
  end
40
43
 
41
44
  def test_array_of_files_different_dir
42
- sources = Array.new
45
+ sources = []
43
46
  sources << File.join(@temp_dir, 'foo', 'file1.mp3')
44
47
  sources << File.join(@temp_dir, 'bar', 'file1.mp3')
45
48
  sources << File.join(@temp_dir, 'baz', 'file1.mp3')
@@ -54,9 +57,9 @@ class TestChannelLocator < MiniTest::Test
54
57
  end
55
58
 
56
59
  def test_array_with_more_than_a_single_directory
57
- Dir.mktmpdir{|tmp_dir1|
58
- Dir.mktmpdir{|tmp_dir2|
59
- sources = Array.new
60
+ Dir.mktmpdir { |tmp_dir1|
61
+ Dir.mktmpdir { |tmp_dir2|
62
+ sources = []
60
63
  sources << File.join(tmp_dir1, 'another_dir')
61
64
  sources << File.join(tmp_dir2, 'another_dir')
62
65
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
  require 'xml/libxml'
3
5
  require 'pry'
@@ -14,7 +16,7 @@ class TestChannelXML < MiniTest::Test
14
16
  # Returnes the XML node for the channel passed as XML string
15
17
  #
16
18
  def channel_node(rss)
17
- XML::Document.string(rss).find("//rss/channel").first
19
+ XML::Document.string(rss).find('//rss/channel').first
18
20
  end
19
21
 
20
22
  #
@@ -27,11 +29,10 @@ class TestChannelXML < MiniTest::Test
27
29
  Dropcaster::Channel.new(FIXTURES_DIR, @options.dup).to_rss
28
30
  end
29
31
 
30
- def test_item
31
- item = @channel.find("item").first
32
+ def test_itunes_item
33
+ item = @channel.find('item[title = "iTunes Name"]').first
32
34
  assert(item)
33
35
 
34
- assert_equal('iTunes Name', item.find('title').first.content)
35
36
  assert_equal('iTunes Artist', item.find('itunes:author', NS_ITUNES).first.content)
36
37
  assert_equal('iTunes Description (Video Pane)', item.find('itunes:summary', NS_ITUNES).first.content)
37
38
  assert_equal('http://www.example.com/podcasts/everything/AllAboutEverything.jpg', item.find('itunes:image', NS_ITUNES).first['href'])
@@ -51,12 +52,31 @@ class TestChannelXML < MiniTest::Test
51
52
  assert_equal('3', item.find('itunes:duration', NS_ITUNES).first.content)
52
53
  end
53
54
 
55
+ def test_special_ampersand_item
56
+ # in the actual XML, this is "special &amp;.mp3", but it gets interpreted by XML::Document
57
+ # if it was just "special &.mp3", it would be invalid XML and we wouldn't get this far
58
+ item = @channel.find('item[title = "test/fixtures/special &.mp3"]').first
59
+ assert(item)
60
+
61
+ enclosure = item.find('enclosure').first
62
+ assert(enclosure)
63
+ assert_equal('http://www.example.com/podcasts/everything/test/fixtures/special%20%26.mp3', enclosure['url'])
64
+ end
65
+
66
+ def test_uppercase_extension_item
67
+ item = @channel.find('item[title = "test/fixtures/extension.MP3"]').first
68
+ assert(item)
69
+
70
+ enclosure = item.find('enclosure').first
71
+ assert(enclosure)
72
+ assert_equal('http://www.example.com/podcasts/everything/test/fixtures/extension.MP3', enclosure['url'])
73
+ end
74
+
54
75
  def test_attributes_mandatory
55
- options = {:title => 'Test Channel',
56
- :url => 'http://www.example.com/',
57
- :description => 'A test channel',
58
- :enclosures_url => 'http://www.example.com/foo/bar',
59
- }
76
+ options = { title: 'Test Channel',
77
+ url: 'http://www.example.com/',
78
+ description: 'A test channel',
79
+ enclosures_url: 'http://www.example.com/foo/bar' }
60
80
 
61
81
  channel = channel_node(Dropcaster::Channel.new(FIXTURES_DIR, options).to_rss)
62
82
  assert_equal('Test Channel', channel.find('title').first.content)
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
 
3
- class TestItem < MiniTest::Test
5
+ class TestItunesItem < MiniTest::Test
4
6
  include DropcasterTest
5
7
 
6
8
  def setup
@@ -12,7 +14,7 @@ class TestItem < MiniTest::Test
12
14
  assert_equal(58119, @item.file_size)
13
15
  assert_equal('77bf84447c0f69ce4a33a18b0ae1e030b82010de', @item.uuid)
14
16
  assert_equal(File.mtime(FIXTURE_ITUNES_MP3).to_i, @item.pub_date.to_i)
15
- assert_equal('test/fixtures/iTunes.mp3', @item.file_name)
17
+ assert_equal('test/fixtures/iTunes.mp3', @item.file_path.to_s)
16
18
  end
17
19
 
18
20
  def test_tag
@@ -38,14 +40,11 @@ class TestItem < MiniTest::Test
38
40
  assert_equal('iTunes Grouping', @item.tag2.TT1)
39
41
  assert_equal('iTunes Description (Video Pane)', @item.tag2.TT3)
40
42
  assert_equal('iTunes Composer', @item.tag2.TCM)
43
+ assert_equal('iTunes Comments (Info Pane)', @item.tag2.COM[1])
41
44
  end
42
45
 
43
46
  def test_lyrics
44
47
  assert_equal(1, @item.lyrics.size)
45
48
  assert_equal("iTunes Lyrics Line 1\niTunes Lyrics Line 2", @item.lyrics['eng'])
46
49
  end
47
-
48
- def test_tag2_comment
49
- assert_equal('iTunes Comments (Info Pane)', @item.tag2.COM[1])
50
- end
51
50
  end
@@ -0,0 +1,2 @@
1
+ _site
2
+ .sass-cache
@@ -0,0 +1,8 @@
1
+ Markdown pages are generated from the README.markdown etc. and then served my Jekyll. Therefore, after each change to the source files, we need to regenerate the pages using the following rake task:
2
+
3
+ rake clobber web:generate
4
+
5
+ Run local test site:
6
+
7
+ # http://jekyllrb.com/docs/github-pages/
8
+ jekyll serve --baseurl ''
@@ -0,0 +1,14 @@
1
+ # Site settings
2
+ title: Dropcaster
3
+ email: nerab@gmx.at
4
+ description: > # this means to ignore newlines until "baseurl:"
5
+ Dropcaster is a podcast feed generator for the command line. It is most
6
+ simple to use with Dropbox, but works equally well with any other (static
7
+ file) web hoster.
8
+ baseurl: "/dropcaster" # the subpath of your site, e.g. /blog/
9
+ # url: "http://yourdomain.com" # the base hostname & protocol for your site
10
+ # twitter_username: jekyllrb
11
+ github_username: nerab
12
+
13
+ # Build settings
14
+ markdown: kramdown
@@ -0,0 +1,5 @@
1
+ ---
2
+ layout: page
3
+ title: Contributing
4
+ permalink: /contributing.html
5
+ ---
@@ -0,0 +1,3 @@
1
+ ---
2
+ layout: page
3
+ ---
@@ -0,0 +1,5 @@
1
+ ---
2
+ layout: page
3
+ title: Vision
4
+ permalink: /vision.html
5
+ ---
@@ -0,0 +1,55 @@
1
+ <footer class="site-footer">
2
+
3
+ <div class="wrapper">
4
+
5
+ <h2 class="footer-heading">{{ site.title }}</h2>
6
+
7
+ <div class="footer-col-wrapper">
8
+ <div class="footer-col footer-col-1">
9
+ <ul class="contact-list">
10
+ <li>{{ site.title }}</li>
11
+ <li><a href="mailto:{{ site.email }}">{{ site.email }}</a></li>
12
+ </ul>
13
+ </div>
14
+
15
+ <div class="footer-col footer-col-2">
16
+ <ul class="social-media-list">
17
+ {% if site.github_username %}
18
+ <li>
19
+ <a href="https://github.com/{{ site.github_username }}">
20
+ <span class="icon icon--github">
21
+ <svg viewBox="0 0 16 16">
22
+ <path fill="#828282" d="M7.999,0.431c-4.285,0-7.76,3.474-7.76,7.761 c0,3.428,2.223,6.337,5.307,7.363c0.388,0.071,0.53-0.168,0.53-0.374c0-0.184-0.007-0.672-0.01-1.32 c-2.159,0.469-2.614-1.04-2.614-1.04c-0.353-0.896-0.862-1.135-0.862-1.135c-0.705-0.481,0.053-0.472,0.053-0.472 c0.779,0.055,1.189,0.8,1.189,0.8c0.692,1.186,1.816,0.843,2.258,0.645c0.071-0.502,0.271-0.843,0.493-1.037 C4.86,11.425,3.049,10.76,3.049,7.786c0-0.847,0.302-1.54,0.799-2.082C3.768,5.507,3.501,4.718,3.924,3.65 c0,0,0.652-0.209,2.134,0.796C6.677,4.273,7.34,4.187,8,4.184c0.659,0.003,1.323,0.089,1.943,0.261 c1.482-1.004,2.132-0.796,2.132-0.796c0.423,1.068,0.157,1.857,0.077,2.054c0.497,0.542,0.798,1.235,0.798,2.082 c0,2.981-1.814,3.637-3.543,3.829c0.279,0.24,0.527,0.713,0.527,1.437c0,1.037-0.01,1.874-0.01,2.129 c0,0.208,0.14,0.449,0.534,0.373c3.081-1.028,5.302-3.935,5.302-7.362C15.76,3.906,12.285,0.431,7.999,0.431z"/>
23
+ </svg>
24
+ </span>
25
+
26
+ <span class="username">{{ site.github_username }}</span>
27
+ </a>
28
+ </li>
29
+ {% endif %}
30
+
31
+ {% if site.twitter_username %}
32
+ <li>
33
+ <a href="https://twitter.com/{{ site.twitter_username }}">
34
+ <span class="icon icon--twitter">
35
+ <svg viewBox="0 0 16 16">
36
+ <path fill="#828282" d="M15.969,3.058c-0.586,0.26-1.217,0.436-1.878,0.515c0.675-0.405,1.194-1.045,1.438-1.809
37
+ c-0.632,0.375-1.332,0.647-2.076,0.793c-0.596-0.636-1.446-1.033-2.387-1.033c-1.806,0-3.27,1.464-3.27,3.27 c0,0.256,0.029,0.506,0.085,0.745C5.163,5.404,2.753,4.102,1.14,2.124C0.859,2.607,0.698,3.168,0.698,3.767 c0,1.134,0.577,2.135,1.455,2.722C1.616,6.472,1.112,6.325,0.671,6.08c0,0.014,0,0.027,0,0.041c0,1.584,1.127,2.906,2.623,3.206 C3.02,9.402,2.731,9.442,2.433,9.442c-0.211,0-0.416-0.021-0.615-0.059c0.416,1.299,1.624,2.245,3.055,2.271 c-1.119,0.877-2.529,1.4-4.061,1.4c-0.264,0-0.524-0.015-0.78-0.046c1.447,0.928,3.166,1.469,5.013,1.469 c6.015,0,9.304-4.983,9.304-9.304c0-0.142-0.003-0.283-0.009-0.423C14.976,4.29,15.531,3.714,15.969,3.058z"/>
38
+ </svg>
39
+ </span>
40
+
41
+ <span class="username">{{ site.twitter_username }}</span>
42
+ </a>
43
+ </li>
44
+ {% endif %}
45
+ </ul>
46
+ </div>
47
+
48
+ <div class="footer-col footer-col-3">
49
+ <p class="text">{{ site.description }}</p>
50
+ </div>
51
+ </div>
52
+
53
+ </div>
54
+
55
+ </footer>
@@ -0,0 +1,11 @@
1
+ <head>
2
+ <meta charset="utf-8">
3
+ <meta name="viewport" content="width=device-width initial-scale=1" />
4
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
5
+
6
+ <title>{{ site.title }}{% if page.title %} - {{ page.title }}{% endif %}</title>
7
+ <meta name="description" content="{{ site.description }}">
8
+
9
+ <link rel="stylesheet" href="{{ "/css/main.css" | prepend: site.baseurl }}">
10
+ <link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}">
11
+ </head>
@@ -0,0 +1,27 @@
1
+ <header class="site-header">
2
+
3
+ <div class="wrapper">
4
+
5
+ <a class="site-title" href="{{ site.baseurl }}/">{{ site.title }}</a>
6
+
7
+ <nav class="site-nav">
8
+ <a href="#" class="menu-icon">
9
+ <svg viewBox="0 0 18 15">
10
+ <path fill="#424242" d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.031C17.335,0,18,0.665,18,1.484L18,1.484z"/>
11
+ <path fill="#424242" d="M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0c0-0.82,0.665-1.484,1.484-1.484 h15.031C17.335,6.031,18,6.696,18,7.516L18,7.516z"/>
12
+ <path fill="#424242" d="M18,13.516C18,14.335,17.335,15,16.516,15H1.484C0.665,15,0,14.335,0,13.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.031C17.335,12.031,18,12.696,18,13.516L18,13.516z"/>
13
+ </svg>
14
+ </a>
15
+
16
+ <div class="trigger">
17
+ {% for page in site.pages %}
18
+ {% if page.title %}
19
+ <a class="page-link" href="{{ page.url | prepend: site.baseurl }}">{{ page.title }}</a>
20
+ {% endif %}
21
+ {% endfor %}
22
+ </div>
23
+ </nav>
24
+
25
+ </div>
26
+
27
+ </header>
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ {% include head.html %}
5
+
6
+ <body>
7
+
8
+ {% include header.html %}
9
+
10
+ <div class="page-content">
11
+ <div class="wrapper">
12
+ {{ content }}
13
+ </div>
14
+ </div>
15
+
16
+ {% include footer.html %}
17
+
18
+ </body>
19
+
20
+ </html>
@@ -0,0 +1,14 @@
1
+ ---
2
+ layout: default
3
+ ---
4
+ <div class="post">
5
+
6
+ <header class="post-header">
7
+ <h1 class="post-title">{{ page.title }}</h1>
8
+ </header>
9
+
10
+ <article class="post-content">
11
+ {{ content }}
12
+ </article>
13
+
14
+ </div>
@@ -0,0 +1,15 @@
1
+ ---
2
+ layout: default
3
+ ---
4
+ <div class="post">
5
+
6
+ <header class="post-header">
7
+ <h1 class="post-title">{{ page.title }}</h1>
8
+ <p class="post-meta">{{ page.date | date: "%b %-d, %Y" }}{% if page.author %} • {{ page.author }}{% endif %}{% if page.meta %} • {{ page.meta }}{% endif %}</p>
9
+ </header>
10
+
11
+ <article class="post-content">
12
+ {{ content }}
13
+ </article>
14
+
15
+ </div>
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Reset some basic elements
3
+ */
4
+ body, h1, h2, h3, h4, h5, h6,
5
+ p, blockquote, pre, hr,
6
+ dl, dd, ol, ul, figure {
7
+ margin: 0;
8
+ padding: 0;
9
+ }
10
+
11
+
12
+
13
+ /**
14
+ * Basic styling
15
+ */
16
+ body {
17
+ font-family: $base-font-family;
18
+ font-size: $base-font-size;
19
+ line-height: $base-line-height;
20
+ font-weight: 300;
21
+ color: $text-color;
22
+ background-color: $background-color;
23
+ -webkit-text-size-adjust: 100%;
24
+ }
25
+
26
+
27
+
28
+ /**
29
+ * Set `margin-bottom` to maintain vertical rhythm
30
+ */
31
+ h1, h2, h3, h4, h5, h6,
32
+ p, blockquote, pre,
33
+ ul, ol, dl, figure,
34
+ %vertical-rhythm {
35
+ margin-bottom: $spacing-unit / 2;
36
+ }
37
+
38
+
39
+
40
+ /**
41
+ * Images
42
+ */
43
+ img {
44
+ max-width: 100%;
45
+ vertical-align: middle;
46
+ }
47
+
48
+
49
+
50
+ /**
51
+ * Figures
52
+ */
53
+ figure > img {
54
+ display: block;
55
+ }
56
+
57
+ figcaption {
58
+ font-size: $small-font-size;
59
+ }
60
+
61
+
62
+
63
+ /**
64
+ * Lists
65
+ */
66
+ ul, ol {
67
+ margin-left: $spacing-unit;
68
+ }
69
+
70
+ li {
71
+ > ul,
72
+ > ol {
73
+ margin-bottom: 0;
74
+ }
75
+ }
76
+
77
+
78
+
79
+ /**
80
+ * Headings
81
+ */
82
+ h1, h2, h3, h4, h5, h6 {
83
+ font-weight: 300;
84
+ }
85
+
86
+
87
+
88
+ /**
89
+ * Links
90
+ */
91
+ a {
92
+ color: $brand-color;
93
+ text-decoration: none;
94
+
95
+ &:visited {
96
+ color: darken($brand-color, 15%);
97
+ }
98
+
99
+ &:hover {
100
+ color: $text-color;
101
+ text-decoration: underline;
102
+ }
103
+ }
104
+
105
+
106
+
107
+ /**
108
+ * Blockquotes
109
+ */
110
+ blockquote {
111
+ color: $grey-color;
112
+ border-left: 4px solid $grey-color-light;
113
+ padding-left: $spacing-unit / 2;
114
+ font-size: 18px;
115
+ letter-spacing: -1px;
116
+ font-style: italic;
117
+
118
+ > :last-child {
119
+ margin-bottom: 0;
120
+ }
121
+ }
122
+
123
+
124
+
125
+ /**
126
+ * Code formatting
127
+ */
128
+ pre,
129
+ code {
130
+ font-size: 15px;
131
+ border: 1px solid $grey-color-light;
132
+ border-radius: 3px;
133
+ background-color: #eef;
134
+ }
135
+
136
+ code {
137
+ padding: 1px 5px;
138
+ }
139
+
140
+ pre {
141
+ padding: 8px 12px;
142
+ overflow-x: scroll;
143
+
144
+ > code {
145
+ border: 0;
146
+ padding-right: 0;
147
+ padding-left: 0;
148
+ }
149
+ }
150
+
151
+
152
+
153
+ /**
154
+ * Wrapper
155
+ */
156
+ .wrapper {
157
+ max-width: -webkit-calc(800px - (#{$spacing-unit} * 2));
158
+ max-width: calc(800px - (#{$spacing-unit} * 2));
159
+ margin-right: auto;
160
+ margin-left: auto;
161
+ padding-right: $spacing-unit;
162
+ padding-left: $spacing-unit;
163
+ @extend %clearfix;
164
+
165
+ @include media-query($on-laptop) {
166
+ max-width: -webkit-calc(800px - (#{$spacing-unit}));
167
+ max-width: calc(800px - (#{$spacing-unit}));
168
+ padding-right: $spacing-unit / 2;
169
+ padding-left: $spacing-unit / 2;
170
+ }
171
+ }
172
+
173
+
174
+
175
+ /**
176
+ * Clearfix
177
+ */
178
+ %clearfix {
179
+
180
+ &:after {
181
+ content: "";
182
+ display: table;
183
+ clear: both;
184
+ }
185
+ }
186
+
187
+
188
+
189
+ /**
190
+ * Icons
191
+ */
192
+ .icon {
193
+
194
+ > svg {
195
+ display: inline-block;
196
+ width: 16px;
197
+ height: 16px;
198
+ vertical-align: middle;
199
+
200
+ path {
201
+ fill: $grey-color;
202
+ }
203
+ }
204
+ }