~srushe/indieweb-post_types

e61eb848071cab750da35552c1d2bd9516eb2779 — Stephen Rushe 2 years ago 00715c1
V0.3.0 (#7)

- Re-organised spec data files.
- Increased test coverage.
- Restore accidently commented-out RSVP code.
- Updated to version 0.3.0.
62 files changed, 2720 insertions(+), 198 deletions(-)

M CHANGELOG.md
M README.md
M indieweb-post_types.gemspec
M lib/indieweb/post_types/identifier/article.rb
M lib/indieweb/post_types/identifier/base.rb
M lib/indieweb/post_types/identifier/rsvp.rb
M lib/indieweb/post_types/version.rb
A spec/examples/entry/article-aaron.json
A spec/examples/entry/article-barryf.json
A spec/examples/entry/article-tantek.json
A spec/examples/entry/like-aaron.json
A spec/examples/entry/like-barryf.json
A spec/examples/entry/note-aaron.json
A spec/examples/entry/note-aaron2.json
A spec/examples/entry/note-micropub-rocks.json
A spec/examples/entry/photo-aaron.json
A spec/examples/entry/photo-tantek.json
A spec/examples/entry/reply-aaron.json
A spec/examples/entry/reply-aaronmultiple.json
A spec/examples/entry/repost-aaron.json
A spec/examples/entry/rsvp-aaron.json
A spec/examples/entry/rsvp-aaronmultiple.json
A spec/examples/entry/rsvp-tantek.json
A spec/examples/entry/video-aaron.json
A spec/examples/entry/video-shane.json
R spec/examples/{article-aaron.json => items/article-aaron.json}
R spec/examples/{article-tantek.json => items/article-tantek.json}
R spec/examples/{like-aaron.json => items/like-aaron.json}
R spec/examples/{note-aaron.json => items/note-aaron.json}
R spec/examples/{note-aaron2.json => items/note-aaron2.json}
R spec/examples/{photo-aaron.json => items/photo-aaron.json}
R spec/examples/{photo-tantek.json => items/photo-tantek.json}
R spec/examples/{reply-aaron.json => items/reply-aaron.json}
R spec/examples/{reply-aaronmultiple.json => items/reply-aaronmultiple.json}
R spec/examples/{repost-aaron.json => items/repost-aaron.json}
R spec/examples/{rsvp-aaron.json => items/rsvp-aaron.json}
R spec/examples/{rsvp-aaronmultiple.json => items/rsvp-aaronmultiple.json}
R spec/examples/{rsvp-tantek.json => items/rsvp-tantek.json}
R spec/examples/{video-aaron.json => items/video-aaron.json}
R spec/examples/{video-shane.json => items/video-shane.json}
A spec/examples/jf2/basic-photo-invalid-url.json
A spec/examples/jf2/basic-photo.json
A spec/examples/jf2/basic-reply.json
A spec/examples/jf2/html-content.json
A spec/examples/jf2/like.json
A spec/examples/jf2/plain-text.json
A spec/examples/jf2/repost.json
A spec/examples/jf2/rsvp.json
A spec/examples/jf2/video.json
A spec/indieweb/post_types/configuration_spec.rb
M spec/indieweb/post_types/identifier/article_spec.rb
M spec/indieweb/post_types/identifier/like_spec.rb
M spec/indieweb/post_types/identifier/note_spec.rb
M spec/indieweb/post_types/identifier/photo_spec.rb
M spec/indieweb/post_types/identifier/reply_spec.rb
M spec/indieweb/post_types/identifier/repost_spec.rb
M spec/indieweb/post_types/identifier/rsvp_spec.rb
M spec/indieweb/post_types/identifier/video_spec.rb
M spec/indieweb/post_types_spec.rb
M spec/spec_helper.rb
M spec/support/example_data.rb
A spec/support/json_for.rb
M CHANGELOG.md => CHANGELOG.md +9 -0
@@ 6,6 6,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

## [0.3.0] - 2018-06-16

### Changed
- Re-organised spec data files.
- Increased test coverage.

### Fixed
- Restore accidently commented-out RSVP code.

## [0.2.0] - 2018-06-12

### Changed

M README.md => README.md +7 -0
@@ 100,3 100,10 @@ The gem is available as open source under the terms of the [MIT License](https:/
## Code of Conduct

Everyone interacting in the Indieweb::PostTypes project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/srushe/indieweb-post_types/blob/master/CODE_OF_CONDUCT.md).

## Credits

A number of the spec example files are from...

* https://github.com/indieweb/post-type-discovery-tests
* https://github.com/grantcodes/jf2-examples

M indieweb-post_types.gemspec => indieweb-post_types.gemspec +1 -0
@@ 30,4 30,5 @@ Gem::Specification.new do |spec|
  spec.add_development_dependency 'rake', '~> 12.3'
  spec.add_development_dependency 'rspec', '~> 3.0'
  spec.add_development_dependency 'rubocop'
  spec.add_development_dependency 'simplecov'
end

M lib/indieweb/post_types/identifier/article.rb => lib/indieweb/post_types/identifier/article.rb +3 -12
@@ 30,7 30,7 @@ module Indieweb
              if content.is_a?(Hash)
                content.any? { |_key, value| !value.strip.empty? }
              else
                content.any? { |entry| !entry.strip.empty? }
                !content.strip.empty?
              end
            end
          end


@@ 42,11 42,7 @@ module Indieweb
          def non_empty?(field, data)
            return false unless data.key?(field)

            if data[field].is_a?(Hash)
              data[field].any? { |_key, value| !value.strip.empty? }
            else
              Array(data[field]).any? { |entry| !entry.strip.empty? }
            end
            Array(data[field]).any? { |entry| !entry.strip.empty? }
          end

          def non_empty_content_or_summary(data)


@@ 73,12 69,7 @@ module Indieweb
          end

          def first_non_empty_for(field, data)
            if data[field].is_a?(Hash)
              possibles = data[field].reject { |_, value| value.strip.empty? }
              possibles.values.first
            else
              Array(data[field]).find { |entry| !entry.strip.empty? }
            end
            Array(data[field]).find { |entry| !entry.strip.empty? }
          end
        end
      end

M lib/indieweb/post_types/identifier/base.rb => lib/indieweb/post_types/identifier/base.rb +1 -1
@@ 31,7 31,7 @@ module Indieweb
        end

        def url_from_citation(data)
          data['properties']['url'][0]
          data['properties'] ? data['properties']['url'][0] : data['url']
        end
      end
    end

M lib/indieweb/post_types/identifier/rsvp.rb => lib/indieweb/post_types/identifier/rsvp.rb +1 -1
@@ 9,7 9,7 @@ module Indieweb

          def identifier_from(data)
            return unless data.key?('rsvp')
            return unless valid_value?(data['rsvp'][0])
            return unless valid_value?(Array(data['rsvp'])[0])

            'rsvp'
          end

M lib/indieweb/post_types/version.rb => lib/indieweb/post_types/version.rb +1 -1
@@ 1,5 1,5 @@
module Indieweb
  module PostTypes
    VERSION = '0.2.0'.freeze
    VERSION = '0.3.0'.freeze
  end
end

A spec/examples/entry/article-aaron.json => spec/examples/entry/article-aaron.json +30 -0
@@ 0,0 1,30 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "name": [
            "German Radio Program"
        ],
        "category": [
            "past-projects",
            "schoolwork",
            "german"
        ],
        "url": [
            "https://aaronparecki.com/2004/12/03/1/german-radio-program"
        ],
        "author": [
            "https://aaronparecki.com/"
        ],
        "published": [
            "2004-12-03T12:00:00-08:00"
        ],
        "content": [
            {
                "html": "<p>This German radio program was a group project of <a href=\"http://twitter.com/aaronpk\">Aaron Parecki</a>, Joel Young, Kirsten Arbogast, <a href=\"http://twitter.com/epicwheelworks\">Jude Kirstein</a>, and Viktoria Wensl\u00f6w during a German 311 course at the University of Oregon. We had a great time putting it together and presenting it, so I figured it deserved a place on the Internet. You can listen to the whole thing here.</p>\n\n<div id=\"album_radio\" class=\"playerContainer\">\n        <div class=\"musicPlayer\" style=\"height:24px\"><object type=\"application/x-shockwave-flash\" name=\"musicPlayer_radio\" style=\"outline: none; visibility: visible;\" data=\"https://aaronparecki.com/assets/player/player.swf\" width=\"440\" height=\"24\" id=\"musicPlayer_radio\"><param name=\"bgcolor\" value=\"#FFFFFF\">\n<param name=\"wmode\" value=\"transparent\">\n<param name=\"menu\" value=\"false\">\n<param name=\"flashvars\" value=\"animation=no&amp;autostart=no&amp;loader=0F3E7F&amp;bg=E3E3E3&amp;soundFile=gr_01_intro.mp3&amp;titles=Introduction, Weather, Traffic&amp;artists=&amp;playerID=musicPlayer_radio\"></object></div>\n        <div class=\"trackRow\" data-filename=\"gr_01_intro.mp3\">\n<span class=\"num\">1</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#introduction-weather-traffic\">Introduction, Weather, Traffic</a></span><span class=\"trackTime\">1:14</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n<div class=\"trackRow\" data-filename=\"gr_02_news.mp3\">\n<span class=\"num\">2</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#news\">News</a></span><span class=\"trackTime\">1:57</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n<div class=\"trackRow\" data-filename=\"gr_03_nutella.mp3\">\n<span class=\"num\">3</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#commercial-nutella\">Commercial: Nutella</a></span><span class=\"trackTime\">0:48</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n<div class=\"trackRow\" data-filename=\"gr_04_flab_buster.mp3\">\n<span class=\"num\">4</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#commercial-flab-buster\">Commercial: Flab Buster</a></span><span class=\"trackTime\">1:14</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n<div class=\"trackRow\" data-filename=\"gr_05_karahasan.mp3\">\n<span class=\"num\">5</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#interview-karahasan\">Interview: Karahasan</a></span><span class=\"trackTime\">2:07</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n<div class=\"trackRow\" data-filename=\"gr_06_lebanese.mp3\">\n<span class=\"num\">6</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#interview-der-lebanese\">Interview: Der Lebanese</a></span><span class=\"trackTime\">2:27</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n<div class=\"trackRow\" data-filename=\"gr_07_regina.mp3\">\n<span class=\"num\">7</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#regina\">Regina</a></span><span class=\"trackTime\">3:17</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n<div class=\"trackRow\" data-filename=\"gr_08_schlafsucht.mp3\">\n<span class=\"num\">8</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#commercial-schlafsucht\">Commercial: Schlafsucht</a></span><span class=\"trackTime\">1:24</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n<div class=\"trackRow\" data-filename=\"gr_09_leica.mp3\">\n<span class=\"num\">9</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#commercial-leica-camera\">Commercial: Leica Camera</a></span><span class=\"trackTime\">1:11</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n<div class=\"trackRow\" data-filename=\"gr_10_cooking.mp3\">\n<span class=\"num\">10</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#cooking-show\">Cooking Show</a></span><span class=\"trackTime\">1:39</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n<div class=\"trackRow\" data-filename=\"gr_11_sportminute.mp3\">\n<span class=\"num\">11</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#sportminute\">Sportminute</a></span><span class=\"trackTime\">1:41</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n<div class=\"trackRow\" data-filename=\"gr_12_mackie_messer.mp3\">\n<span class=\"num\">12</span> <span class=\"title\"><a href=\"https://aaronparecki.com/2004/12/03/1/german-radio-program#mackie-messer\">Mackie Messer</a></span><span class=\"trackTime\">2:36</span><img class=\"trackPlayButton\" src=\"https://aaronparecki.com/assets/player/control_play_blue.png\" alt=\"Click to play!\" style=\"cursor: pointer;\">\n</div>\n        <div class=\"artist\"></div>\n        <div class=\"album\"></div>\n</div>\n\n<h2>Introduction, Weather, Traffic</h2>\n\n<p>Good morning Bavaria. It's going to be a cloudy day, but don't give up hope immediately, we expect the sun to break thorough early this afternoon. All you commuters better perk your ears up, because coming up next is pertinent info to your drive. Our weather man is reporting from our helicopter. \"Are you there, Rolf?\" \"Yeah, I'm up here like a star in the sky.\" \"What do you have regarding the traffic?\" \"Just recently an traffic jam has built up on the south-bound lane of the Munich-Garmisch-Partenkirchen freeway. If I were you, I'd go somewhere else, if you're headin' south. Also, a terrible accident just occurred in downtown Munich. Three people have been hurt. The driver has both legs broken. The passenger suffers from pleurisy. The pedestrian they ran over fights for his life in the hospital. Hopefully his current one-foot-in-the-grave state won't last long. That's it, until later.\" \"Thanks Rolf, you're the king of your own world, we'll never be able to understand.\"</p>\n\n<ul>\n<li>unter bedecktem Himmel stehen, to be overcast outside; cloudy</li>\n<li>der Pendler, commuter</li>\n<li>der Hubschrauber, helicopter</li>\n<li>der Stau, traffic jam</li>\n<li>die Rippenfellentz\u00fcndung, pluersy (difficulty breathing)</li>\n<li>der F\u00fcssg\u00e4nger, pedestrian</li>\n</ul>\n<h2>News</h2>\n\n<p>A new German beer claims that it slows down the aging process, either by drinking it or dabbing it on one's skin. Like any other beer, this one can lead to drunkenness and therefore also a hangover, due to it 4.8% alcohol content. Interestingly enough the production of this beer could be illegal under the oldest law in the world, which was made in 1516 and asserts that \"beer\" can only contain four ingredients: Hops, barley, yeast and water. This issue will be decided in a court of law and if it is found that it cannot be called beer, then they will have to label it as something else. As for whether or not it really does work as an anti-aging tonic, if the brew doesn't do anything when you dab it on your skin, you can always go the traditional route and use it to drown your sorrows.</p>\n\n<ul>\n<li>verlangsamen, to slow down</li>\n<li>die Einleitung, introduction</li>\n<li>avisiert sein, to be advised</li>\n<li>das Gesetz, law</li>\n<li>der Prozess, trial</li>\n</ul>\n<h2>Commercial: Nutella</h2>\n\n<p>A commercial for Nutella. Two announcers believe they can do a serious commercial about Nutella but as they attempt to do so they get swept away into their fantasies about Nutella.</p>\n\n<ul>\n<li>Messer, knife</li>\n<li>sahnige, creamy</li>\n<li>steichen, to spread</li>\n<li>Schicht, layer</li>\n</ul>\n<h2>Commercial: Flab Buster</h2>\n\n<p>A commercial for an electronic device that burns fat for you so you don't have to.</p>\n\n<ul>\n<li>abnehmen, to lose weight</li>\n<li>das Gewicht, weight</li>\n<li>Ultraschallimpulse, ultrasonic pulses</li>\n<li>bombardieren, to bombard</li>\n<li>der Staubsauger, vaccuum cleaner</li>\n<li>absaugen, to suck off</li>\n<li>verursachen, to cause</li>\n<li>die Testergebnisse, test results</li>\n<li>auftreten, to appear</li>\n<li>der Ausschlag, rash</li>\n</ul>\n<h2>Interview: Karahasan</h2>\n\n<p>This is \"Poetry Time\" for the radio station, and the poet reading his poems today is the author of the story \"Heimat: Tiefer, gr\u00f6sser, weiter als wir selbst\". In this section we wanted to summarize the author's point of view as it came across in the story, but also capitalizing on the way he told it, VERY DRAMATICALLY. The poems he has supposedly written are very simple, yet extremely melodramatic. But even though it is meant to be funny, it still summarizes the author's actual feelings as told in his short story. The basic format is an announcer conducting a brief interview with a famous German poet, and is followed by the poet reading two poems of his homeland out loud.</p>\n\n<ul>\n<li>der Bach, stream</li>\n<li>die Erinnerungen, memories</li>\n<li>die Vergangenheit, past</li>\n<li>die Gedichte, poems</li>\n<li>vorlesen, to read to</li>\n</ul>\n<h2>Interview: Der Lebanese</h2>\n\n<p>A reporter shows up on the scene to find a Lebanese man trying to move into his house but can't because a bookcase is standing before his doorway. The reporter asks the Lebanese man how he feels and to no surprise finds out that the Lebanese man feels unwelcome. To Herr Helmut it feels like his duty. The reporter realizes what he's saying and feels she needs to do something about it and so she decides to call all Germans together to literally embrace/hug all foreigners. The irony in this piece is that the reporter never gets to know the Lebanese Man's name and consistently refers to him as a foreigner or an outlandish person. She doesn't realize how unwelcoming she herself is.</p>\n\n<ul>\n<li>B\u00fcchershrank, bookcase</li>\n<li>br\u00fcllt, screams</li>\n<li>Wahrheit, truth</li>\n<li>umarmen, to hug</li>\n</ul>\n<h2>Regina</h2>\n\n<p>Ekkerhard: \"We've got two journalists in the studio, who are interested in the relationships between German women and American men married couples. First, we're going to listen to an interview they've conducted with Regina in Berlin. Now if I may introduce them, the first is Hildegard and for a long time she's worked for the S\u00fcddeutsche Zeitung here in Munich as a Reporter and also as an Editor. Her specialty, so to speak, is in psychological matters. Her sidekick, Monika, is a Germanistik student.</p>\n\n<p>Ekkerhard: \"So, what did you find out?\"</p>\n\n<p>Hildegard: \"Regina met her Man when he was a soldier stationed in Berlin. He helped her when she negligently ran her car out of gas.\"</p>\n\n<p>Monika: \"One of my colleagues developed this machine, which is really a type of computer that can tell us what our victim is actually thinking, therefore we receive accurate answers, instead of what they think we want to hear. The following is what we received from Regina's thoughts:\"</p>\n\n<p>\"I really do think that all American men are cowboys. It's really too bad John Wayne existed, but what can I do (to change that), nothing I guess. These two women are really annoying. The old bag on the left looks like a goat and smells worse. They'll never get anything out of me.</p>\n\n<p>Godlike voice: \"Is your life really so topsy-turvy.\"</p>\n\n<p>Regina's thoughts continue: \"Shit, they've posed another one of their disparaging questions... These electrodes are painful. Monika would be a satisfactory lover, if she \"really\" could be lesbian, but most of these young girls only have the courage to kiss another and not much more. I'd be more satisfied with Arthur if he didn't always behave like a such a cowboy. Whenever I ask him what he's going to do, I get the same answer. \"I'm stickin' to my guns girl, it's the code of the west.\" What the hell's that supposed to mean. He really is a victim of his passion. I should start smoking again. The kids are almost exclusively speaking English because of these blasted Disney films and I'm no longer part of the picture. The small time crook, who plays the great messiah could snuff Arthur, but I couldn't afford to pay him off. I guess I could pay with my body, but that'd be disgusting.</p>\n\n<p>Monika: \"Alright, that's it.\"</p>\n\n<p>Regina: \"That wasn't so bad, it went quicker than I thought it would.\"</p>\n\n<ul>\n<li>die Hinsicht, aspect</li>\n<li>gewohnt, familiar, accustomed</li>\n<li>der Redaktuer/die Redaktuerin, editor</li>\n<li>der Handlanger/die Handlangerin, sidekick</li>\n<li>tats\u00e4chlich, actually</li>\n<li>sozusagen, so to speak, as it were</li>\n<li>vorw\u00e4rts, onward, forward, ahead</li>\n<li>das Ger\u00e4t, device, implement, apparatus</li>\n<li>die alte Tasche, literally - the old bag</li>\n<li>die Ziege, goat</li>\n<li>merkw\u00fcrdigeliebe, Strangelove</li>\n<li>st\u00e4ndig, constantly</li>\n<li>die Z\u00e4rtlichkeit, endearment, fondness</li>\n<li>die Leidenschaft, passion, fervor</li>\n<li>der Ganove, crook, hood, racketeer</li>\n<li>der grosse Heilsbringer, the great messsiah</li>\n</ul>\n<h2>Commercial: Schlafsucht</h2>\n\n<p>Sleep is a drug that everyone is addicted to! It is the cause of more than 400,000 car accidents per year. \"Gro\u00dfen Augen\" is here to cure you of your sleep addiction.</p>\n\n<ul>\n<li>anerkannt, recognized</li>\n<li>die Schlafsucht, sleep addiction</li>\n<li>die Hauptursache, main cause</li>\n<li>verlorene, lost</li>\n<li>Fabriken, factories</li>\n<li>verantwortlich, responsible</li>\n<li>verschwenden, to waste</li>\n<li>Symptome des Entzugs, withdrawal symptoms</li>\n<li>h\u00e4ufigsten, most frequent</li>\n<li>offensichtlichsten, most obvious</li>\n<li>blutunterlaufene Augen, bloodshot eyes</li>\n<li>das Urteilsverm\u00f6gen, judgement</li>\n<li>beherrschen, to control</li>\n</ul>\n<h2>Commercial: Leica Camera</h2>\n\n<p>The new Leica SLR camera grants the highest possible quality in every aspect and in the use of the user. The Leica R9 is designed specifically to the needs of ambitious photographers. It follows the hitherto Leica philosophy that the camera can instinctively be used without removing it from the eye of the user. Also, the new camera back designed for the Leica R8 and R9 models can turn it into a digital camera, which makes it the first, and only, hybrid camera on the market today. Now you can take pictures in digital and analog in the familiar Leica quality you have grown to lust over.</p>\n\n<ul>\n<li>vielseitige, versatile</li>\n<li>Anwenders, users</li>\n<li>Bed\u00fcrfnissen, needs</li>\n<li>entwickeln, to develop</li>\n<li>bedienen, to operate</li>\n</ul>\n<h2>Cooking Show</h2>\n\n<p>A recipe for Chestnut Butter Soup. Ironically the edition is called \"Our Healthy German Kitchen\" but nothing about the recipe is savory let alone healthy.</p>\n\n<ul>\n<li>Ausgabe, edition</li>\n<li>Nebengericht, side dish</li>\n</ul>\n<h2>Sportminute</h2>\n\n<p>The latest scores from soccer. In Formula 1, Michael Schumacher had an accident, and is for the first time in his career in 18th place in the finals. In Table Tennis, Germany qualified for the Austrian Open, where they will play Hong Kong.</p>\n\n<ul>\n<li>Nachrichten, news</li>\n<li>Bundesliga, national league</li>\n<li>Tabellenspitze, top of the charts</li>\n<li>besiegen, to defeat</li>\n<li>rangiert, ranked</li>\n<li>achtbester, eighth best</li>\n<li>steigen, to rise</li>\n</ul>\n<h2>Mackie Messer</h2>\n\n<p>\"Mackie Messer\" (Mack the Knife) with Joel singing and Aaron on the accordion!</p>\n\n<p><script type=\"text/javascript\" src=\"https://aaronparecki.com/assets/player/audio-player.js\"></script><script type=\"text/javascript\" src=\"https://aaronparecki.com/assets/player/musicplayer.js\"></script><link rel=\"stylesheet\" type=\"text/css\" href=\"https://aaronparecki.com/assets/player/styles.css\"></p>\n",
                "value": "This German radio program was a group project of Aaron Parecki, Joel Young, Kirsten Arbogast, Jude Kirstein, and Viktoria Wensl\u00f6w during a German 311 course at the University of Oregon. We had a great time putting it together and presenting it, so I figured it deserved a place on the Internet. You can listen to the whole thing here. \n\n\n         \n        1 Introduction, Weather, Traffic1:14Click to play! \n2 News1:57Click to play! \n3 Commercial: Nutella0:48Click to play! \n4 Commercial: Flab Buster1:14Click to play! \n5 Interview: Karahasan2:07Click to play! \n6 Interview: Der Lebanese2:27Click to play! \n7 Regina3:17Click to play! \n8 Commercial: Schlafsucht1:24Click to play! \n9 Commercial: Leica Camera1:11Click to play! \n10 Cooking Show1:39Click to play! \n11 Sportminute1:41Click to play! \n12 Mackie Messer2:36Click to play! \n         \n         \n \n\nIntroduction, Weather, Traffic \n\nGood morning Bavaria. It's going to be a cloudy day, but don't give up hope immediately, we expect the sun to break thorough early this afternoon. All you commuters better perk your ears up, because coming up next is pertinent info to your drive. Our weather man is reporting from our helicopter. \"Are you there, Rolf?\" \"Yeah, I'm up here like a star in the sky.\" \"What do you have regarding the traffic?\" \"Just recently an traffic jam has built up on the south-bound lane of the Munich-Garmisch-Partenkirchen freeway. If I were you, I'd go somewhere else, if you're headin' south. Also, a terrible accident just occurred in downtown Munich. Three people have been hurt. The driver has both legs broken. The passenger suffers from pleurisy. The pedestrian they ran over fights for his life in the hospital. Hopefully his current one-foot-in-the-grave state won't last long. That's it, until later.\" \"Thanks Rolf, you're the king of your own world, we'll never be able to understand.\" \n\nunter bedecktem Himmel stehen, to be overcast outside; cloudy \nder Pendler, commuter \nder Hubschrauber, helicopter \nder Stau, traffic jam \ndie Rippenfellentz\u00fcndung, pluersy (difficulty breathing) \nder F\u00fcssg\u00e4nger, pedestrian \n News \n\nA new German beer claims that it slows down the aging process, either by drinking it or dabbing it on one's skin. Like any other beer, this one can lead to drunkenness and therefore also a hangover, due to it 4.8% alcohol content. Interestingly enough the production of this beer could be illegal under the oldest law in the world, which was made in 1516 and asserts that \"beer\" can only contain four ingredients: Hops, barley, yeast and water. This issue will be decided in a court of law and if it is found that it cannot be called beer, then they will have to label it as something else. As for whether or not it really does work as an anti-aging tonic, if the brew doesn't do anything when you dab it on your skin, you can always go the traditional route and use it to drown your sorrows. \n\nverlangsamen, to slow down \ndie Einleitung, introduction \navisiert sein, to be advised \ndas Gesetz, law \nder Prozess, trial \n Commercial: Nutella \n\nA commercial for Nutella. Two announcers believe they can do a serious commercial about Nutella but as they attempt to do so they get swept away into their fantasies about Nutella. \n\nMesser, knife \nsahnige, creamy \nsteichen, to spread \nSchicht, layer \n Commercial: Flab Buster \n\nA commercial for an electronic device that burns fat for you so you don't have to. \n\nabnehmen, to lose weight \ndas Gewicht, weight \nUltraschallimpulse, ultrasonic pulses \nbombardieren, to bombard \nder Staubsauger, vaccuum cleaner \nabsaugen, to suck off \nverursachen, to cause \ndie Testergebnisse, test results \nauftreten, to appear \nder Ausschlag, rash \n Interview: Karahasan \n\nThis is \"Poetry Time\" for the radio station, and the poet reading his poems today is the author of the story \"Heimat: Tiefer, gr\u00f6sser, weiter als wir selbst\". In this section we wanted to summarize the author's point of view as it came across in the story, but also capitalizing on the way he told it, VERY DRAMATICALLY. The poems he has supposedly written are very simple, yet extremely melodramatic. But even though it is meant to be funny, it still summarizes the author's actual feelings as told in his short story. The basic format is an announcer conducting a brief interview with a famous German poet, and is followed by the poet reading two poems of his homeland out loud. \n\nder Bach, stream \ndie Erinnerungen, memories \ndie Vergangenheit, past \ndie Gedichte, poems \nvorlesen, to read to \n Interview: Der Lebanese \n\nA reporter shows up on the scene to find a Lebanese man trying to move into his house but can't because a bookcase is standing before his doorway. The reporter asks the Lebanese man how he feels and to no surprise finds out that the Lebanese man feels unwelcome. To Herr Helmut it feels like his duty. The reporter realizes what he's saying and feels she needs to do something about it and so she decides to call all Germans together to literally embrace/hug all foreigners. The irony in this piece is that the reporter never gets to know the Lebanese Man's name and consistently refers to him as a foreigner or an outlandish person. She doesn't realize how unwelcoming she herself is. \n\nB\u00fcchershrank, bookcase \nbr\u00fcllt, screams \nWahrheit, truth \numarmen, to hug \n Regina \n\nEkkerhard: \"We've got two journalists in the studio, who are interested in the relationships between German women and American men married couples. First, we're going to listen to an interview they've conducted with Regina in Berlin. Now if I may introduce them, the first is Hildegard and for a long time she's worked for the S\u00fcddeutsche Zeitung here in Munich as a Reporter and also as an Editor. Her specialty, so to speak, is in psychological matters. Her sidekick, Monika, is a Germanistik student. \n\nEkkerhard: \"So, what did you find out?\" \n\nHildegard: \"Regina met her Man when he was a soldier stationed in Berlin. He helped her when she negligently ran her car out of gas.\" \n\nMonika: \"One of my colleagues developed this machine, which is really a type of computer that can tell us what our victim is actually thinking, therefore we receive accurate answers, instead of what they think we want to hear. The following is what we received from Regina's thoughts:\" \n\n\"I really do think that all American men are cowboys. It's really too bad John Wayne existed, but what can I do (to change that), nothing I guess. These two women are really annoying. The old bag on the left looks like a goat and smells worse. They'll never get anything out of me. \n\nGodlike voice: \"Is your life really so topsy-turvy.\" \n\nRegina's thoughts continue: \"Shit, they've posed another one of their disparaging questions... These electrodes are painful. Monika would be a satisfactory lover, if she \"really\" could be lesbian, but most of these young girls only have the courage to kiss another and not much more. I'd be more satisfied with Arthur if he didn't always behave like a such a cowboy. Whenever I ask him what he's going to do, I get the same answer. \"I'm stickin' to my guns girl, it's the code of the west.\" What the hell's that supposed to mean. He really is a victim of his passion. I should start smoking again. The kids are almost exclusively speaking English because of these blasted Disney films and I'm no longer part of the picture. The small time crook, who plays the great messiah could snuff Arthur, but I couldn't afford to pay him off. I guess I could pay with my body, but that'd be disgusting. \n\nMonika: \"Alright, that's it.\" \n\nRegina: \"That wasn't so bad, it went quicker than I thought it would.\" \n\ndie Hinsicht, aspect \ngewohnt, familiar, accustomed \nder Redaktuer/die Redaktuerin, editor \nder Handlanger/die Handlangerin, sidekick \ntats\u00e4chlich, actually \nsozusagen, so to speak, as it were \nvorw\u00e4rts, onward, forward, ahead \ndas Ger\u00e4t, device, implement, apparatus \ndie alte Tasche, literally - the old bag \ndie Ziege, goat \nmerkw\u00fcrdigeliebe, Strangelove \nst\u00e4ndig, constantly \ndie Z\u00e4rtlichkeit, endearment, fondness \ndie Leidenschaft, passion, fervor \nder Ganove, crook, hood, racketeer \nder grosse Heilsbringer, the great messsiah \n Commercial: Schlafsucht \n\nSleep is a drug that everyone is addicted to! It is the cause of more than 400,000 car accidents per year. \"Gro\u00dfen Augen\" is here to cure you of your sleep addiction. \n\nanerkannt, recognized \ndie Schlafsucht, sleep addiction \ndie Hauptursache, main cause \nverlorene, lost \nFabriken, factories \nverantwortlich, responsible \nverschwenden, to waste \nSymptome des Entzugs, withdrawal symptoms \nh\u00e4ufigsten, most frequent \noffensichtlichsten, most obvious \nblutunterlaufene Augen, bloodshot eyes \ndas Urteilsverm\u00f6gen, judgement \nbeherrschen, to control \n Commercial: Leica Camera \n\nThe new Leica SLR camera grants the highest possible quality in every aspect and in the use of the user. The Leica R9 is designed specifically to the needs of ambitious photographers. It follows the hitherto Leica philosophy that the camera can instinctively be used without removing it from the eye of the user. Also, the new camera back designed for the Leica R8 and R9 models can turn it into a digital camera, which makes it the first, and only, hybrid camera on the market today. Now you can take pictures in digital and analog in the familiar Leica quality you have grown to lust over. \n\nvielseitige, versatile \nAnwenders, users \nBed\u00fcrfnissen, needs \nentwickeln, to develop \nbedienen, to operate \n Cooking Show \n\nA recipe for Chestnut Butter Soup. Ironically the edition is called \"Our Healthy German Kitchen\" but nothing about the recipe is savory let alone healthy. \n\nAusgabe, edition \nNebengericht, side dish \n Sportminute \n\nThe latest scores from soccer. In Formula 1, Michael Schumacher had an accident, and is for the first time in his career in 18th place in the finals. In Table Tennis, Germany qualified for the Austrian Open, where they will play Hong Kong. \n\nNachrichten, news \nBundesliga, national league \nTabellenspitze, top of the charts \nbesiegen, to defeat \nrangiert, ranked \nachtbester, eighth best \nsteigen, to rise \n Mackie Messer \n\n\"Mackie Messer\" (Mack the Knife) with Joel singing and Aaron on the accordion!"
            }
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/article-barryf.json => spec/examples/entry/article-barryf.json +31 -0
@@ 0,0 1,31 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "content": [
            "My wife and I are expecting our second baby in the new year. Our first, now a toddler, loved his [Ewan The Dream Sheep](https://www.sweetdreamers.co.uk/product/ewan-the-dream-sheep/) at night, especially its gentle harp lullaby music, but the short duration (20 minutes) meant we were frequently restarting the music to help him fully drift off.\r\n\r\nI wanted a better solution this time around with a song that lasts for hours. All the off-the-shelf baby products I found were similarly limited. I looked through several cheap Bluetooth speakers, but I didn't want to have to leave my phone connected all day/night long. The [Anker SoundCore Mini](https://www.anker.com/uk/products/variant/SoundCore-Mini-Bluetooth-Speaker/A3101111), however, also accepts a microSD card so can be left to play independently. I ordered one from Amazon for a very reasonable £16.99 and a SanDisk 16Gb microSD card (with SD adapter to go in my Mac) for £6.50. \r\n\r\nNext I needed a lullaby track lasting for at least 12 hours, thinking optimistically that the baby may sleep that long. Ignoring all the spammy-looking \"free download MP3 baby lullaby\" links on Google, I decided to make my own. \r\n\r\nI first paid for and downloaded a gentle piano recording of [Brahm's Lullaby from iTunes](https://itunes.apple.com/gb/album/brahms-lullaby/1065946325?i=1065946793) on my Mac. I converted the M4A/AAC file to an MP3 in iTunes (File > Convert > Create MP3 Version) -- the SoundCore doesn't play AAC files -- and then copied the MP3 file to my desktop as `lullaby.mp3`.\r\n\r\nThis created a single song lasting 150 seconds, but I needed this to loop. So I installed [ffmpeg](http://ffmpeg.org) to process and convert the audio. I specified the LAME MP3 library to create a final MP3 file.\r\n\r\n    $ brew install ffmpeg --with-libmp3lame\r\n\r\nThe ~150s track played 300 times would result in approximately 12.5 hours of music. The following command specifies 300 loops, takes an input of the original lullaby and uses the LAME library at the same 190kbit/s quality of the original (`qscale:a 2`). \r\n\r\n    $ ffmpeg -stream_loop 300 -i lullaby.mp3 -codec:a libmp3lame -qscale:a 2 lullaby-loop.mp3\r\n\r\nAfter a few minutes of processing I had a large (~1Gb) file ready to transfer via the SD card adapter to the microSD card and the SoundCore speaker. One extra I needed to add was a USB-A charger to power the speaker, but I had one spare lying around so that wasn't an issue. It all works beautifully. \r\n\r\nNow let's see if this all pays off and the little monkey sleeps when they arrive!\r\n"
        ],
        "category": [
            "baby",
            "music",
            "ffmpeg"
        ],
        "entry-type": [
            "article"
        ],
        "name": [
            "Making a looping lullaby box"
        ],
        "published": [
            "2017-12-29T11:27:46Z"
        ],
        "updated": [
            "2017-12-29T11:38:05Z"
        ],
        "syndication": [
            "https://twitter.com/barryf/status/946706289018785793",
            "https://medium.com/@barryf/making-a-looping-lullaby-box-311673d1d692"
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/article-tantek.json => spec/examples/entry/article-tantek.json +50 -0
@@ 0,0 1,50 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "author": [
            {
                "type": [
                    "h-card"
                ],
                "properties": {
                    "name": [
                        "Tantek \u00c7elik"
                    ],
                    "photo": [
                        "http://tantek.com/logo.jpg"
                    ],
                    "url": [
                        "http://tantek.com/"
                    ]
                },
                "value": "Tantek \u00c7elik"
            }
        ],
        "name": [
            "Bringing Back The Blog"
        ],
        "url": [
            "http://tantek.com/2010/231/b1/bringing-back-the-blog"
        ],
        "uid": [
            "http://tantek.com/2010/231/b1/bringing-back-the-blog"
        ],
        "syndication": [
            "http://twitter.com/t/status/21599383183"
        ],
        "published": [
            "2010-08-19T11:21-0700"
        ],
        "updated": [
            "2010-08-19T11:21-0700"
        ],
        "content": [
            {
                "html": "\n<p>\n8 years ago this month I <a href=\"http://tantek.com/log/2002/08.html#L20020808\">started blogging</a> my thoughts on tantek.com. That lasted just 6 years; there was no <q cite=\"http://tantek.com/log/2008/08.html#d08t1923\"><a href=\"http://tantek.com/log/2008/08.html#d08t1923\">seventh year of blogging</a></q>.\n</p>\n<p>\nI continued publicly posting to <a href=\"http://twitter.com/t\">Twitter</a>, Pownce (moment of silence), <a href=\"http://flic.kr/tantek\">Flickr</a> (currently 7 months behind), <a href=\"http://tantek.pbworks.com/\">my PBWiki</a>, and comments on others' blogs. But it wasn't the same. I had gone from owning (most of) my content, to <a href=\"http://www.codinghorror.com/blog/2009/08/are-you-a-digital-sharecropper.html\">digital sharecropping</a>.\n</p>\n<p>\nThe past two years I watched <a href=\"http://waxy.org/2008/02/cnets_consumati/\">lifechanging</a>, <a href=\"http://pownce.com/\">brilliant</a>, and some <a href=\"http://geocities.com/\">longlived</a> sites get <a href=\"http://adactio.com/journal/1621/\">killed</a> by <a href=\"http://googlecode.blogspot.com/2009/01/changes-for-jaiku-and-farewell-to.html\">owners</a> that <a href=\"http://etherpad.com/transition.html\">knew not</a> what they had, or just <a href=\"http://tr.im/\">gave up</a>. Even <a href=\"http://twitter.com/\">Twitter</a>, one of my favorites, was <a href=\"http://www.flickr.com/photos/factoryjoe/3927590515/\">unreliable enough</a>, often enough, to be frustrating. \n</p>\n<p>\nLast year I told myself <em>this</em> year would be different. Among personal struggles perhaps I'll recount someday, I thought about how <em>should</em> personal publishing work? How did <em>I</em> want it to work?\n</p>\n<p>\nInspired by the <a href=\"http://status.net/\">works of others</a>, yet focused on scratching my own itch, I finally built <a href=\"http://tantek.com/w/Falcon\">Falcon</a> and <a href=\"http://tantek.com/2010/026/t2/tweeting-from-tantek-com\">launched it</a> on tantek.com this past January (using the open source <a href=\"http://cassisproject.com\">CASSIS</a> language/framework), and subsequently  conceptualized my thoughts on <a href=\"http://tantek.com/2010/034/t2/diso-2-personal-domains-shortener-hatom-push-relmeauth\">the future of independent publishing</a>.\n</p>\n<p>\nEncouraged by the energy, enthusiasm, and optimism of the implementers at the <a href=\"http://federatedsocialweb.net/wiki/Federated_Social_Web_Summit_2010\">Federated Social Web Summit</a> (<a href=\"http://federatedsocialweb.net/wiki/2010-199-tantek-fsws-talk\">presentation</a>) a month ago, I decided little text notes were not enough, it was time to rebuild my blog.\n</p>\n<p>\nIt's simple, it's undesigned, and it's got plenty that can be improved.  But I can now write posts, have Falcon show them on my <a href=\"http://tantek.com\">home page</a> and their own <a href=\"http://tantek.com/2010/231/b1/bringing-back-the-blog\">permalink</a> pages, <a href=\"http://pubsubhubbub.googlecode.com/\">PuSH</a> them with a multi-object-type <a href=\"http://activitystrea.ms/\">Activity Stream</a> to realtime subscribers, and snowflake syndicate them to <a href=\"http://twitter.com/t\">Twitter</a>.\n</p>\n<p>\nIt's a start.\n</p>\n",
                "value": "8 years ago this month I started blogging my thoughts on tantek.com. That lasted just 6 years; there was no seventh year of blogging.\n \nI continued publicly posting to Twitter, Pownce (moment of silence), Flickr (currently 7 months behind), my PBWiki, and comments on others' blogs. But it wasn't the same. I had gone from owning (most of) my content, to digital sharecropping.\n \nThe past two years I watched lifechanging, brilliant, and some longlived sites get killed by owners that knew not what they had, or just gave up. Even Twitter, one of my favorites, was unreliable enough, often enough, to be frustrating. \n \nLast year I told myself this year would be different. Among personal struggles perhaps I'll recount someday, I thought about how should personal publishing work? How did I want it to work?\n \nInspired by the works of others, yet focused on scratching my own itch, I finally built Falcon and launched it on tantek.com this past January (using the open source CASSIS language/framework), and subsequently  conceptualized my thoughts on the future of independent publishing.\n \nEncouraged by the energy, enthusiasm, and optimism of the implementers at the Federated Social Web Summit (presentation) a month ago, I decided little text notes were not enough, it was time to rebuild my blog.\n \nIt's simple, it's undesigned, and it's got plenty that can be improved.  But I can now write posts, have Falcon show them on my home page and their own permalink pages, PuSH them with a multi-object-type Activity Stream to realtime subscribers, and snowflake syndicate them to Twitter.\n \nIt's a start."
            }
        ]
    }
}

A spec/examples/entry/like-aaron.json => spec/examples/entry/like-aaron.json +25 -0
@@ 0,0 1,25 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "content": [
            "https://indiewebcat.com/notes/2014/12/23/3/businesscatual"
        ],
        "name": [
            "https://indiewebcat.com/notes/2014/12/23/3/businesscatual"
        ],
        "like-of": [
            "https://indiewebcat.com/notes/2014/12/23/3/businesscatual"
        ],
        "url": [
            "https://aaronparecki.com/2014/12/26/8/"
        ],
        "author": [
            "https://aaronparecki.com/"
        ],
        "published": [
            "2014-12-26T16:15:10-08:00"
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/like-barryf.json => spec/examples/entry/like-barryf.json +22 -0
@@ 0,0 1,22 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "like-of": [
            "https://keithjgrant.com/posts/2018/06/resilient-declarative-contextual/"
        ],
        "category": [
            "css"
        ],
        "captures": [

        ],
        "entry-type": [
            "like"
        ],
        "published": [
            "2018-06-12T15:00:15Z"
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/note-aaron.json => spec/examples/entry/note-aaron.json +51 -0
@@ 0,0 1,51 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "location": [
            {
                "type": [
                    "h-card"
                ],
                "properties": {
                    "name": [
                        "Portland, OR, US"
                    ],
                    "latitude": [
                        "45.528786"
                    ],
                    "longitude": [
                        "-122.681402"
                    ]
                },
                "value": "Portland, OR, US"
            }
        ],
        "name": [
            "Alright, if all goes well, this message will be posted to my site and syndicated to Twitter, app.net, and broadcast via PuSH. #indieweb"
        ],
        "category": [
            "indieweb",
            "indieweb"
        ],
        "url": [
            "https://aaronparecki.com/2012/08/19/2/indieweb"
        ],
        "syndication": [
            "https://twitter.com/aaronpk/status/237344101928951808"
        ],
        "author": [
            "https://aaronparecki.com/"
        ],
        "published": [
            "2012-08-19T17:23:36-07:00"
        ],
        "content": [
            {
                "html": "Alright, if all goes well, this message will be posted to my site and syndicated to Twitter, app.net, and broadcast via PuSH. <a href=\"https://aaronparecki.com/tag/indieweb\">#<span class=\"p-category\">indieweb</span></a>",
                "value": "Alright, if all goes well, this message will be posted to my site and syndicated to Twitter, app.net, and broadcast via PuSH. #indieweb"
            }
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/note-aaron2.json => spec/examples/entry/note-aaron2.json +611 -0
@@ 0,0 1,611 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "location": [
            {
                "type": [
                    "h-adr"
                ],
                "properties": {
                    "locality": [
                        "Portland"
                    ],
                    "region": [
                        "Oregon"
                    ],
                    "country": [
                        "USA"
                    ],
                    "latitude": [
                        "45.55488"
                    ],
                    "longitude": [
                        "-122.68202"
                    ],
                    "name": [
                        "Portland,\n        Oregon,\n        USA"
                    ]
                },
                "value": "Portland,\n        Oregon,\n        USA"
            }
        ],
        "like": [
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "nahbois"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/3133d790cbaf037424348bf80f30026e2e8fb382bcda8b25d868901a99414ac4.jpg"
                                ],
                                "url": [
                                    "https://twitter.com/nahbois"
                                ]
                            },
                            "value": "nahbois"
                        }
                    ],
                    "url": [
                        "https://twitter.com/aaronpk/status/880965166799519748#favorited-by-16260229"
                    ],
                    "name": [
                        "nahbois"
                    ]
                },
                "value": "nahbois"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Julien Genestoux"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/1c4eefaae056b2962c2038838168f28ddd447dc4bffdee800db75387db4a8914.jpeg"
                                ],
                                "url": [
                                    "https://twitter.com/julien51"
                                ]
                            },
                            "value": "Julien Genestoux"
                        }
                    ],
                    "url": [
                        "https://twitter.com/aaronpk/status/880965166799519748#favorited-by-5381582"
                    ],
                    "name": [
                        "Julien Genestoux"
                    ]
                },
                "value": "Julien Genestoux"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Shane Becker"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/ff8865c08331abfd4bdeef5505069a2c73a03d5b16f470a9da4bf901ff6cc258.png"
                                ],
                                "url": [
                                    "https://twitter.com/veganstraightedge"
                                ]
                            },
                            "value": "Shane Becker"
                        }
                    ],
                    "url": [
                        "https://twitter.com/aaronpk/status/880965166799519748#favorited-by-641013"
                    ],
                    "name": [
                        "Shane Becker"
                    ]
                },
                "value": "Shane Becker"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Sean Wiese"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/7128d0335b8a0b3f1a0b070d9502a68047e4956b0896b2e68741bfcd80cb535c.jpeg"
                                ],
                                "url": [
                                    "https://twitter.com/dswiese"
                                ]
                            },
                            "value": "Sean Wiese"
                        }
                    ],
                    "url": [
                        "https://twitter.com/aaronpk/status/880965166799519748#favorited-by-12466762"
                    ],
                    "name": [
                        "Sean Wiese"
                    ]
                },
                "value": "Sean Wiese"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "TomWithTheWeather"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/ce4f2f04b015ad5bd826584ceac5e6dbf0c8c1b9be2bdce80df7bc7a1d83bdcd.jpeg"
                                ],
                                "url": [
                                    "https://twitter.com/tomwiththeweath"
                                ]
                            },
                            "value": "TomWithTheWeather"
                        }
                    ],
                    "url": [
                        "https://twitter.com/aaronpk/status/880965166799519748#favorited-by-961661"
                    ],
                    "name": [
                        "TomWithTheWeather"
                    ]
                },
                "value": "TomWithTheWeather"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Mele \ud83d\ude44 \ud83d\udcbb"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/b0ff7bfae7d867db5e475db22348889b8a3d678e2ced837f635b1b619f5e7326.jpg"
                                ],
                                "url": [
                                    "https://twitter.com/pdxmele"
                                ]
                            },
                            "value": "Mele \ud83d\ude44 \ud83d\udcbb"
                        }
                    ],
                    "url": [
                        "https://twitter.com/aaronpk/status/880965166799519748#favorited-by-141771323"
                    ],
                    "name": [
                        "Mele \ud83d\ude44 \ud83d\udcbb"
                    ]
                },
                "value": "Mele \ud83d\ude44 \ud83d\udcbb"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Andrew"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/b52f10a9f000253f87c3849668a786dbaed9709ef89d1294e933bec404c26051.jpeg"
                                ],
                                "url": [
                                    "https://twitter.com/dalglsh"
                                ]
                            },
                            "value": "Andrew"
                        }
                    ],
                    "url": [
                        "https://twitter.com/aaronpk/status/880965166799519748#favorited-by-14303647"
                    ],
                    "name": [
                        "Andrew"
                    ]
                },
                "value": "Andrew"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Joel Purra"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/33e14bc2eff5caae419d9a852891106d3b5888e7b87c849211a2ac35764cb0f1.jpg"
                                ],
                                "url": [
                                    "https://twitter.com/joelpurra"
                                ]
                            },
                            "value": "Joel Purra"
                        }
                    ],
                    "url": [
                        "https://twitter.com/aaronpk/status/880965166799519748#favorited-by-203610224"
                    ],
                    "name": [
                        "Joel Purra"
                    ]
                },
                "value": "Joel Purra"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Mitch Kiah"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/c1cc85a977eeeaaa040d479de4945ef5e963e86b39203ce2984086385d1d7400.jpg"
                                ],
                                "url": [
                                    "https://twitter.com/MitchKiah"
                                ]
                            },
                            "value": "Mitch Kiah"
                        }
                    ],
                    "url": [
                        "https://twitter.com/aaronpk/status/880965166799519748#favorited-by-872328169935843329"
                    ],
                    "name": [
                        "Mitch Kiah"
                    ]
                },
                "value": "Mitch Kiah"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Paint My Boat"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/66faa62146e1419422a65d704efb76c37065b9dc81699e245fd3ab5bf54e41b8.png"
                                ],
                                "url": [
                                    "https://twitter.com/PMyB"
                                ]
                            },
                            "value": "Paint My Boat"
                        }
                    ],
                    "url": [
                        "https://twitter.com/aaronpk/status/880965166799519748#favorited-by-31983245"
                    ],
                    "name": [
                        "Paint My Boat"
                    ]
                },
                "value": "Paint My Boat"
            }
        ],
        "repost": [
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Rob Schoening"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/2ad4e0c1a4bbdb6fee1ea1a92e039d21f285142fd5d027c6ebd404ecba7a06eb.jpg"
                                ],
                                "url": [
                                    "https://twitter.com/rschoening"
                                ]
                            },
                            "value": "Rob Schoening"
                        }
                    ],
                    "url": [
                        "https://twitter.com/rschoening/status/880991341626273794"
                    ],
                    "name": [
                        "Rob Schoening"
                    ]
                },
                "value": "Rob Schoening"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Andrew Jacobs"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/www.funwhilelost.com/de8faa65e1539a0970bce9cab66e0de3973289241990c1bc955813083810df6f.jpg"
                                ],
                                "url": [
                                    "http://www.funwhilelost.com/profile/funwhilelost"
                                ]
                            },
                            "value": "Andrew Jacobs"
                        }
                    ],
                    "url": [
                        "http://www.funwhilelost.com/2017/repost-of-aaron-pareckis-post-on-aaronpareckicom-fa58ce8442"
                    ],
                    "name": [
                        "Andrew Jacobs"
                    ]
                },
                "value": "Andrew Jacobs"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Andrew Jacobs"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/www.funwhilelost.com/de8faa65e1539a0970bce9cab66e0de3973289241990c1bc955813083810df6f.jpg"
                                ],
                                "url": [
                                    "https://www.funwhilelost.com/profile/funwhilelost"
                                ]
                            },
                            "value": "Andrew Jacobs"
                        }
                    ],
                    "url": [
                        "https://www.funwhilelost.com/2017/repost-of-aaron-pareckis-post-on-aaronpareckicom-fa58ce8442"
                    ],
                    "name": [
                        "Andrew Jacobs"
                    ]
                },
                "value": "Andrew Jacobs"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Amy MacKinnon"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/27d1969974be29f5887ed96999e2ba60c9226d80738d9894d98f53ce301a84f8.jpeg"
                                ],
                                "url": [
                                    "http://amy-mac.com"
                                ]
                            },
                            "value": "Amy MacKinnon"
                        }
                    ],
                    "url": [
                        "https://twitter.com/ThatAmyMac/status/881572717136236544"
                    ],
                    "name": [
                        "Amy MacKinnon"
                    ]
                },
                "value": "Amy MacKinnon"
            }
        ],
        "comment": [
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Andy Baio"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/d47673417b019bdbc0f6fb21fc7a57c1e3c3fd941bb1cbeeedd4ac4dd4252995.png"
                                ],
                                "url": [
                                    "http://waxy.org/"
                                ]
                            },
                            "value": "Andy Baio"
                        }
                    ],
                    "name": [
                        "well, they're old NOW"
                    ],
                    "url": [
                        "https://twitter.com/waxpancake/status/880982978611912706"
                    ],
                    "published": [
                        "2017-07-01T02:54:44+00:00"
                    ],
                    "content": [
                        {
                            "html": "well, they're old NOW",
                            "value": "well, they're old NOW"
                        }
                    ]
                },
                "value": "well, they're old NOW"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Torgie Madison"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/bbe35a62c0425df2cbe0d2f9c1a7133cea05ded16557398a69b1e0ae71325594.jpg"
                                ],
                                "url": [
                                    "https://twitter.com/torgie"
                                ]
                            },
                            "value": "Torgie Madison"
                        }
                    ],
                    "name": [
                        "Implied: if you used angelfire, you aren't old. Yusss"
                    ],
                    "url": [
                        "https://twitter.com/torgie/status/881053452192817153"
                    ],
                    "published": [
                        "2017-07-01T07:34:46+00:00"
                    ],
                    "content": [
                        {
                            "html": "Implied: if you used angelfire, you aren't old. Yusss",
                            "value": "Implied: if you used angelfire, you aren't old. Yusss"
                        }
                    ]
                },
                "value": "Implied: if you used angelfire, you aren't old. Yusss"
            }
        ],
        "name": [
            "OH: \"Geocities? I don't really know what that is. I just know that old people used it.\" \ud83d\ude2d\ud83d\ude2d\ud83d\ude2d"
        ],
        "category": [
            "indieweb"
        ],
        "url": [
            "https://aaronparecki.com/2017/06/30/24/indieweb"
        ],
        "syndication": [
            "https://twitter.com/aaronpk/status/880965166799519748"
        ],
        "author": [
            "https://aaronparecki.com/"
        ],
        "published": [
            "2017-06-30T18:43:54-07:00"
        ],
        "content": [
            {
                "html": "OH: \"Geocities? I don't really know what that is. I just know that old people used it.\" <a href=\"https://aaronparecki.com/emoji/%F0%9F%98%AD\" class=\"emoji\">\ud83d\ude2d</a><a href=\"https://aaronparecki.com/emoji/%F0%9F%98%AD\" class=\"emoji\">\ud83d\ude2d</a><a href=\"https://aaronparecki.com/emoji/%F0%9F%98%AD\" class=\"emoji\">\ud83d\ude2d</a>",
                "value": "OH: \"Geocities? I don't really know what that is. I just know that old people used it.\" \ud83d\ude2d\ud83d\ude2d\ud83d\ude2d"
            }
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/note-micropub-rocks.json => spec/examples/entry/note-micropub-rocks.json +13 -0
@@ 0,0 1,13 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "content": [
            "Micropub test of creating a basic h-entry"
        ],
        "published": [
            "2018-06-13T08:32:22Z"
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/photo-aaron.json => spec/examples/entry/photo-aaron.json +367 -0
@@ 0,0 1,367 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "location": [
            {
                "type": [
                    "h-card"
                ],
                "properties": {
                    "name": [
                        "Computer History Museum"
                    ],
                    "latitude": [
                        "37.414534"
                    ],
                    "longitude": [
                        "-122.077517"
                    ]
                },
                "value": "Computer History Museum"
            }
        ],
        "like": [
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Amber Case"
                                ],
                                "photo": [
                                    "https://distillery.s3.amazonaws.com/profiles/profile_5792358_75sq_1364955062.jpg"
                                ],
                                "url": [
                                    "http://instagram.com/caseorganic"
                                ]
                            },
                            "value": "Amber Case"
                        }
                    ],
                    "url": [
                        "http://instagram.com/p/nqkp7qjcj2/"
                    ],
                    "name": [
                        "Amber Case"
                    ]
                },
                "value": "Amber Case"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Ben Michel"
                                ],
                                "photo": [
                                    "https://pbs.twimg.com/profile_images/444713131986526208/ZdcDTjKu.jpeg"
                                ],
                                "url": [
                                    "http://twitter.com/obensource"
                                ]
                            },
                            "value": "Ben Michel"
                        }
                    ],
                    "url": [
                        "http://twitter.com/aaronpk/status/463747044260065280"
                    ],
                    "name": [
                        "Ben Michel"
                    ]
                },
                "value": "Ben Michel"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Tantek \u00c7elik"
                                ],
                                "photo": [
                                    "https://distillery.s3.amazonaws.com/profiles/profile_2975244_75sq_1390152350.jpg"
                                ],
                                "url": [
                                    "http://instagram.com/tantek"
                                ]
                            },
                            "value": "Tantek \u00c7elik"
                        }
                    ],
                    "url": [
                        "http://instagram.com/p/nqkp7qjcj2/"
                    ],
                    "name": [
                        "Tantek \u00c7elik"
                    ]
                },
                "value": "Tantek \u00c7elik"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Tantek \u00c7elik"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/38f635b64f6add618dfb72c1f115322b8c92777ddc5efd2784806c2d3f20df2e.jpeg"
                                ],
                                "url": [
                                    "http://twitter.com/t"
                                ]
                            },
                            "value": "Tantek \u00c7elik"
                        }
                    ],
                    "url": [
                        "http://twitter.com/aaronpk/status/463747044260065280"
                    ],
                    "name": [
                        "Tantek \u00c7elik"
                    ]
                },
                "value": "Tantek \u00c7elik"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Charlie O'Keefe"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/bf365a113c0276c636d7c9ce60c6fedccc0b441c019dc74e5649cd5ded563ba4.jpeg"
                                ],
                                "url": [
                                    "http://twitter.com/charlieok"
                                ]
                            },
                            "value": "Charlie O'Keefe"
                        }
                    ],
                    "url": [
                        "http://twitter.com/aaronpk/status/463747044260065280"
                    ],
                    "name": [
                        "Charlie O'Keefe"
                    ]
                },
                "value": "Charlie O'Keefe"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Ben Michel"
                                ],
                                "photo": [
                                    "https://distillery.s3.amazonaws.com/profiles/profile_14104640_75sq_1396284103.jpg"
                                ],
                                "url": [
                                    "http://instagram.com/obensource"
                                ]
                            },
                            "value": "Ben Michel"
                        }
                    ],
                    "url": [
                        "http://instagram.com/p/nqkp7qjcj2/"
                    ],
                    "name": [
                        "Ben Michel"
                    ]
                },
                "value": "Ben Michel"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Shane Becker"
                                ],
                                "photo": [
                                    "https://distillery.s3.amazonaws.com/profiles/profile_1403229_75sq_1386973867.jpg"
                                ],
                                "url": [
                                    "http://instagram.com/veganstraightedge"
                                ]
                            },
                            "value": "Shane Becker"
                        }
                    ],
                    "url": [
                        "http://instagram.com/p/nqkp7qjcj2/"
                    ],
                    "name": [
                        "Shane Becker"
                    ]
                },
                "value": "Shane Becker"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Mike Goggin"
                                ],
                                "photo": [
                                    "https://pbs.twimg.com/profile_images/2678215087/4719f4ee7059ad759d4e1600a0cdb920.jpeg"
                                ],
                                "url": [
                                    "http://twitter.com/MichaelGoggin"
                                ]
                            },
                            "value": "Mike Goggin"
                        }
                    ],
                    "url": [
                        "http://twitter.com/aaronpk/status/463747044260065280"
                    ],
                    "name": [
                        "Mike Goggin"
                    ]
                },
                "value": "Mike Goggin"
            }
        ],
        "comment": [
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Amber Case"
                                ],
                                "photo": [
                                    "https://distillery.s3.amazonaws.com/profiles/profile_5792358_75sq_1364955062.jpg"
                                ],
                                "url": [
                                    "http://instagram.com/caseorganic"
                                ]
                            },
                            "value": "Amber Case"
                        }
                    ],
                    "name": [
                        "Hi Ben!"
                    ],
                    "url": [
                        "http://instagram.com/p/nqkp7qjcj2/"
                    ],
                    "published": [
                        "2014-05-06T11:28:48-07:00"
                    ],
                    "content": [
                        {
                            "html": "Hi Ben!",
                            "value": "Hi Ben!"
                        }
                    ]
                },
                "value": "Hi Ben!"
            }
        ],
        "name": [
            "Session scheduling #iiw #indieweb"
        ],
        "category": [
            "iiw",
            "indieweb",
            "iiw",
            "indieweb"
        ],
        "photo": [
            "https://aaronparecki.com/2014/05/06/12/photo.jpeg"
        ],
        "url": [
            "https://aaronparecki.com/2014/05/06/12/iiw-indieweb"
        ],
        "syndication": [
            "http://instagram.com/p/nqkp7qjcj2/",
            "https://twitter.com/aaronpk/status/463747044260065280"
        ],
        "author": [
            "https://aaronparecki.com/"
        ],
        "published": [
            "2014-05-06T11:19:48-07:00"
        ],
        "content": [
            {
                "html": "Session scheduling <a href=\"https://aaronparecki.com/tag/iiw\">#<span class=\"p-category\">iiw</span></a> <a href=\"https://aaronparecki.com/tag/indieweb\">#<span class=\"p-category\">indieweb</span></a>",
                "value": "Session scheduling #iiw #indieweb"
            }
        ]
    }
}

A spec/examples/entry/photo-tantek.json => spec/examples/entry/photo-tantek.json +59 -0
@@ 0,0 1,59 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "author": [
            {
                "type": [
                    "h-card"
                ],
                "properties": {
                    "name": [
                        "Tantek \u00c7elik"
                    ],
                    "photo": [
                        "http://tantek.com/logo.jpg"
                    ],
                    "url": [
                        "http://tantek.com/"
                    ]
                },
                "value": "Tantek \u00c7elik"
            }
        ],
        "name": [
            "a photo. \nDid entire #NPSF #trackattack:\n1600 1200 1000 800 600 400 abs.\n\nPrev: tantek.com/2015/230/t1/npsf-trackish\nPrev completion: tantek.com/2015/118/t1/npsf-trackattack-done"
        ],
        "category": [
            "NPSF",
            "trackattack:"
        ],
        "syndication": [
            "https://instagram.com/p/7GEfT3g9Vo",
            "https://www.facebook.com/photo.php?fbid=10101862796233213",
            "https://twitter.com/t/status/638755285159559168"
        ],
        "photo": [
            "https://scontent-sjc2-1.cdninstagram.com/hphotos-xaf1/t51.2885-15/e35/11821356_395494483975922_1481558253_n.jpg"
        ],
        "url": [
            "http://tantek.com/2015/244/t1/did-entire-npsf-trackattack"
        ],
        "uid": [
            "http://tantek.com/2015/244/t1/did-entire-npsf-trackattack"
        ],
        "published": [
            "2015-09-01T09:48-0700"
        ],
        "updated": [
            "2015-09-01T09:48-0700"
        ],
        "content": [
            {
                "html": "<a class=\"auto-link figure u-syndication\" href=\"https://instagram.com/p/7GEfT3g9Vo\"><img class=\"auto-embed u-photo\" alt=\"a photo. \" src=\"https://scontent-sjc2-1.cdninstagram.com/hphotos-xaf1/t51.2885-15/e35/11821356_395494483975922_1481558253_n.jpg\"></a><br class=\"auto-break\">Did entire #<span class=\"p-category auto-tag\">NPSF</span> #<span class=\"p-category auto-tag\">trackattack:</span><br class=\"auto-break\">1600 1200 1000 800 600 400 abs.<br class=\"auto-break\"><br class=\"auto-break\">Prev: <a class=\"auto-link\" href=\"http://tantek.com/2015/230/t1/npsf-trackish\">tantek.com/2015/230/t1/npsf-trackish</a><br class=\"auto-break\">Prev completion: <a class=\"auto-link\" href=\"http://tantek.com/2015/118/t1/npsf-trackattack-done\">tantek.com/2015/118/t1/npsf-trackattack-done</a>",
                "value": "a photo. \nDid entire #NPSF #trackattack:\n1600 1200 1000 800 600 400 abs.\n\nPrev: tantek.com/2015/230/t1/npsf-trackish\nPrev completion: tantek.com/2015/118/t1/npsf-trackattack-done"
            }
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/reply-aaron.json => spec/examples/entry/reply-aaron.json +51 -0
@@ 0,0 1,51 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "location": [
            {
                "type": [
                    "h-card"
                ],
                "properties": {
                    "name": [
                        "Portland, OR, USA"
                    ],
                    "latitude": [
                        "45.58878"
                    ],
                    "longitude": [
                        "-122.593759"
                    ]
                },
                "value": "Portland, OR, USA"
            }
        ],
        "name": [
            "@eschnou It worked! Now here's a reply! #indieweb"
        ],
        "category": [
            "indieweb",
            "indieweb"
        ],
        "in-reply-to": [
            "http://eschnou.com/entry/testing-indieweb-federation-with-waterpigscouk-aaronpareckicom-and--62-24908.html"
        ],
        "url": [
            "https://aaronparecki.com/2013/04/19/3/indieweb"
        ],
        "author": [
            "https://aaronparecki.com/"
        ],
        "published": [
            "2013-04-19T11:35:50-07:00"
        ],
        "content": [
            {
                "html": "<a href=\"https://twitter.com/eschnou\">@eschnou</a> It worked! Now here's a reply! <a href=\"https://aaronparecki.com/tag/indieweb\">#<span class=\"p-category\">indieweb</span></a>",
                "value": "@eschnou It worked! Now here's a reply! #indieweb"
            }
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/reply-aaronmultiple.json => spec/examples/entry/reply-aaronmultiple.json +34 -0
@@ 0,0 1,34 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "name": [
            "@dybskiy @skjain2 @oscargodson @helloerica Guess you guys are right! I'll try that next time. Have you heard of anybody actually doing that?"
        ],
        "in-reply-to": [
            "https://twitter.com/dybskiy/status/326063595332984832",
            "https://twitter.com/skjain2/status/326063776510144514",
            "https://twitter.com/oscargodson/status/326063992047034368",
            "https://twitter.com/helloerica/status/326064591417274368"
        ],
        "url": [
            "https://aaronparecki.com/2013/04/21/3/"
        ],
        "syndication": [
            "https://twitter.com/aaronpk/status/326065365589311490"
        ],
        "author": [
            "https://aaronparecki.com/"
        ],
        "published": [
            "2013-04-21T13:10:15-07:00"
        ],
        "content": [
            {
                "html": "<a href=\"https://twitter.com/dybskiy\">@dybskiy</a> <a href=\"https://twitter.com/skjain2\">@skjain2</a> <a href=\"https://twitter.com/oscargodson\">@oscargodson</a> <a href=\"https://twitter.com/helloerica\">@helloerica</a> Guess you guys are right! I'll try that next time. Have you heard of anybody actually doing that?",
                "value": "@dybskiy @skjain2 @oscargodson @helloerica Guess you guys are right! I'll try that next time. Have you heard of anybody actually doing that?"
            }
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/repost-aaron.json => spec/examples/entry/repost-aaron.json +142 -0
@@ 0,0 1,142 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "repost-of": [
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Justin Richer"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/pbs.twimg.com/e38cdf4c922259c2e4252cfdd49640976bfb102cde4281013618c536f44e5fb0.jpg"
                                ],
                                "url": [
                                    "http://bspk.io/"
                                ]
                            },
                            "value": "Justin Richer"
                        }
                    ],
                    "name": [
                        "The article on OAuth and Authentication that I helped write/edit is online now: http://oauth.net/articles/authentication/ (thanks to @aaronpk for publishing!)"
                    ],
                    "url": [
                        "https://twitter.com/zer0n1ne/status/529126352633344001"
                    ],
                    "content": [
                        {
                            "html": "The article on OAuth and Authentication that I helped write/edit is online now: <a href=\"http://oauth.net/articles/authentication/\"><span class=\"protocol\">http://</span>oauth.net/articles/authentication/</a> (thanks to <a href=\"https://twitter.com/aaronpk\">@aaronpk</a> for publishing!)",
                            "value": "The article on OAuth and Authentication that I helped write/edit is online now: http://oauth.net/articles/authentication/ (thanks to @aaronpk for publishing!)"
                        }
                    ]
                },
                "value": "https://twitter.com/zer0n1ne/status/529126352633344001"
            }
        ],
        "comment": [
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Aaron Parecki"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/aaronparecki.com/315c3e5ec79692096afd23002ac6860e55387ebf3ee157ae2b7ef5d21ee75c9d.png"
                                ],
                                "url": [
                                    "https://aaronparecki.com/"
                                ]
                            },
                            "value": "Aaron Parecki"
                        }
                    ],
                    "name": [
                        "Owning My Data in 2014"
                    ],
                    "url": [
                        "https://aaronparecki.com/articles/2015/01/04/1/owning-my-data-in-2014"
                    ],
                    "published": [
                        "2015-01-04T15:05:00-08:00"
                    ]
                },
                "value": "Owning My Data in 2014"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Aaron Parecki"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/aaronparecki.com/2b8e1668dcd9cfa6a170b3724df740695f73a15c2a825962fd0a0967ec11ecdc.jpeg"
                                ],
                                "url": [
                                    "https://aaronparecki.com/"
                                ]
                            },
                            "value": "Aaron Parecki"
                        }
                    ],
                    "name": [
                        "Owning My Data in 2014"
                    ],
                    "url": [
                        "https://aaronparecki.com/2015/01/04/9/owning-my-data-in-2014"
                    ],
                    "published": [
                        "2015-01-04T15:05:00-08:00"
                    ]
                },
                "value": "Owning My Data in 2014"
            }
        ],
        "name": [
            ""
        ],
        "category": [
            "oauth",
            "oauth2"
        ],
        "url": [
            "https://aaronparecki.com/2014/11/02/22/oauth2"
        ],
        "syndication": [
            "https://twitter.com/aaronpk/status/529127377495154688"
        ],
        "author": [
            "https://aaronparecki.com/"
        ],
        "published": [
            "2014-11-02T23:22:07-05:00",
            "2014-11-02T20:25:00-08:00"
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/rsvp-aaron.json => spec/examples/entry/rsvp-aaron.json +53 -0
@@ 0,0 1,53 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "location": [
            {
                "type": [
                    "h-card"
                ],
                "properties": {
                    "name": [
                        "Portland, OR, USA"
                    ],
                    "latitude": [
                        "45.524058"
                    ],
                    "longitude": [
                        "-122.675565"
                    ]
                },
                "value": "Portland, OR, USA"
            }
        ],
        "rsvp": [
            "yes"
        ],
        "name": [
            "@benwerd Sounds like fun! I'm in!"
        ],
        "category": [
            "indiewebcamp"
        ],
        "in-reply-to": [
            "http://werd.io/event/51c94deebed7de5c2386a1b4/unofficial-portland-post-indiewebcamp-meetup"
        ],
        "url": [
            "https://aaronparecki.com/2013/06/25/4/indiewebcamp"
        ],
        "author": [
            "https://aaronparecki.com/"
        ],
        "published": [
            "2013-06-25T17:49:55-07:00"
        ],
        "content": [
            {
                "html": "<a href=\"https://twitter.com/benwerd\">@benwerd</a> Sounds like fun! I'm in!",
                "value": "@benwerd Sounds like fun! I'm in!"
            }
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/rsvp-aaronmultiple.json => spec/examples/entry/rsvp-aaronmultiple.json +51 -0
@@ 0,0 1,51 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "location": [
            {
                "type": [
                    "h-card"
                ],
                "properties": {
                    "name": [
                        "Portland, Oregon, USA"
                    ],
                    "latitude": [
                        "45.506746"
                    ],
                    "longitude": [
                        "-122.654171"
                    ]
                },
                "value": "Portland, Oregon, USA"
            }
        ],
        "rsvp": [
            "yes"
        ],
        "name": [
            "I'll be there!"
        ],
        "in-reply-to": [
            "http://known.kevinmarks.com/2014/indieweb-xoxo-breakfast",
            "http://aaronparecki.com/events/2014/09/13/1/indieweb-xoxo-breakfast"
        ],
        "url": [
            "https://aaronparecki.com/2014/09/08/6/"
        ],
        "author": [
            "https://aaronparecki.com/"
        ],
        "published": [
            "2014-09-08T12:54:08-07:00"
        ],
        "content": [
            {
                "html": "I'll be there!",
                "value": "I'll be there!"
            }
        ]
    }
}

A spec/examples/entry/rsvp-tantek.json => spec/examples/entry/rsvp-tantek.json +166 -0
@@ 0,0 1,166 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "in-reply-to": [
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "name": [
                        "http://aaronparecki.com/events/2013/09/21/1/indiewebcamp-breakfast"
                    ],
                    "url": [
                        "http://aaronparecki.com/events/2013/09/21/1/indiewebcamp-breakfast"
                    ]
                },
                "value": "http://aaronparecki.com/events/2013/09/21/1/indiewebcamp-breakfast"
            }
        ],
        "author": [
            {
                "type": [
                    "h-card"
                ],
                "properties": {
                    "name": [
                        "Tantek \u00c7elik"
                    ],
                    "photo": [
                        "http://tantek.com/logo.jpg"
                    ],
                    "url": [
                        "http://tantek.com/"
                    ]
                },
                "value": "Tantek \u00c7elik"
            }
        ],
        "rsvp": [
            "yes"
        ],
        "name": [
            "going to #IndieWebCamp breakfast 9am @theoriginalpdx cc: @benwerd @blaine @daltonc @evanpro @kevinmarks RSVP: http://aaronparecki.com/events/2013/09/21/1/indiewebcamp-breakfast\nP.S. Just implemented #indieweb RSVP \"yes\" in @Falcon this morning. This is my first RSVP post. Details:\n* indiewebcamp.com/rsvp\n* ufs.cc/w/rsvp-brainstorming#h-entry_plus_additions"
        ],
        "category": [
            "IndieWebCamp",
            "indieweb"
        ],
        "url": [
            "http://tantek.com/2013/264/t1/indiewebcamp-breakfast-rsvp"
        ],
        "uid": [
            "http://tantek.com/2013/264/t1/indiewebcamp-breakfast-rsvp"
        ],
        "syndication": [
            "http://twitter.com/t/status/381440402500648960"
        ],
        "published": [
            "2013-09-21T08:30-0700"
        ],
        "updated": [
            "2013-09-21T08:30-0700"
        ],
        "content": [
            {
                "html": "going to #<span class=\"p-category auto-tag\">IndieWebCamp</span> breakfast 9am <a class=\"auto-link h-cassis-username\" href=\"https://twitter.com/theoriginalpdx\">@theoriginalpdx</a> cc: <a class=\"auto-link h-cassis-username\" href=\"https://twitter.com/benwerd\">@benwerd</a> <a class=\"auto-link h-cassis-username\" href=\"https://twitter.com/blaine\">@blaine</a> <a class=\"auto-link h-cassis-username\" href=\"https://twitter.com/daltonc\">@daltonc</a> <a class=\"auto-link h-cassis-username\" href=\"https://twitter.com/evanpro\">@evanpro</a> <a class=\"auto-link h-cassis-username\" href=\"https://twitter.com/kevinmarks\">@kevinmarks</a> RSVP: <a class=\"auto-link\" href=\"http://aaronparecki.com/events/2013/09/21/1/indiewebcamp-breakfast\">http://aaronparecki.com/events/2013/09/21/1/indiewebcamp-breakfast</a><br class=\"auto-break\">P.S. Just implemented #<span class=\"p-category auto-tag\">indieweb</span> RSVP \"yes\" in <a class=\"auto-link h-cassis-username\" href=\"https://twitter.com/Falcon\">@Falcon</a> this morning. This is my first RSVP post. Details:<br class=\"auto-break\">* <a class=\"auto-link\" href=\"http://indiewebcamp.com/rsvp\">indiewebcamp.com/rsvp</a><br class=\"auto-break\">* <a class=\"auto-link\" href=\"http://ufs.cc/w/rsvp-brainstorming#h-entry_plus_additions\">ufs.cc/w/rsvp-brainstorming#h-entry_plus_additions</a>",
                "value": "going to #IndieWebCamp breakfast 9am @theoriginalpdx cc: @benwerd @blaine @daltonc @evanpro @kevinmarks RSVP: http://aaronparecki.com/events/2013/09/21/1/indiewebcamp-breakfast\nP.S. Just implemented #indieweb RSVP \"yes\" in @Falcon this morning. This is my first RSVP post. Details:\n* indiewebcamp.com/rsvp\n* ufs.cc/w/rsvp-brainstorming#h-entry_plus_additions"
            }
        ]
    },
    "children": [
        {
            "type": [
                "h-cassis-username"
            ],
            "properties": {
                "name": [
                    "@theoriginalpdx"
                ],
                "url": [
                    "https://twitter.com/theoriginalpdx"
                ]
            }
        },
        {
            "type": [
                "h-cassis-username"
            ],
            "properties": {
                "name": [
                    "@benwerd"
                ],
                "url": [
                    "https://twitter.com/benwerd"
                ]
            }
        },
        {
            "type": [
                "h-cassis-username"
            ],
            "properties": {
                "name": [
                    "@blaine"
                ],
                "url": [
                    "https://twitter.com/blaine"
                ]
            }
        },
        {
            "type": [
                "h-cassis-username"
            ],
            "properties": {
                "name": [
                    "@daltonc"
                ],
                "url": [
                    "https://twitter.com/daltonc"
                ]
            }
        },
        {
            "type": [
                "h-cassis-username"
            ],
            "properties": {
                "name": [
                    "@evanpro"
                ],
                "url": [
                    "https://twitter.com/evanpro"
                ]
            }
        },
        {
            "type": [
                "h-cassis-username"
            ],
            "properties": {
                "name": [
                    "@kevinmarks"
                ],
                "url": [
                    "https://twitter.com/kevinmarks"
                ]
            }
        },
        {
            "type": [
                "h-cassis-username"
            ],
            "properties": {
                "name": [
                    "@Falcon"
                ],
                "url": [
                    "https://twitter.com/Falcon"
                ]
            }
        }
    ]
}
\ No newline at end of file

A spec/examples/entry/video-aaron.json => spec/examples/entry/video-aaron.json +471 -0
@@ 0,0 1,471 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "location": [
            {
                "type": [
                    "h-card"
                ],
                "properties": {
                    "name": [
                        "Portland International  Airport"
                    ],
                    "latitude": [
                        "45.589158"
                    ],
                    "longitude": [
                        "-122.594457"
                    ]
                },
                "value": "Portland International  Airport"
            }
        ],
        "like": [
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "The B. Side"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/igcdn-photos-c-a.akamaihd.net/5134077cd4ca6d3e5368cbcc0dfdb4bc075fe848e9d4ed625994373d08af9cdf.jpeg"
                                ],
                                "url": [
                                    "http://instagram.com/thebsuit"
                                ]
                            },
                            "value": "The B. Side"
                        }
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#liked-by-1094499841"
                    ],
                    "name": [
                        "The B. Side"
                    ]
                },
                "value": "The B. Side"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Jeff Bunch"
                                ],
                                "photo": [
                                    "https://igcdn-photos-h-a.akamaihd.net/hphotos-ak-xap1/t51.2885-19/10623824_719383001432039_1285870441_a.jpg"
                                ],
                                "url": [
                                    "http://instagram.com/jeffreyrbunch"
                                ]
                            },
                            "value": "Jeff Bunch"
                        }
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#liked-by-990727"
                    ],
                    "name": [
                        "Jeff Bunch"
                    ]
                },
                "value": "Jeff Bunch"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "tali_engelen"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/igcdn-photos-c-a.akamaihd.net/3afb17bb13406425bb8dfb8766cc3e2165408e237424c9a535a9c1c02575cc97.jpeg"
                                ],
                                "url": [
                                    "http://instagram.com/tali_engelen"
                                ]
                            },
                            "value": "tali_engelen"
                        }
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#liked-by-183365259"
                    ],
                    "name": [
                        "tali_engelen"
                    ]
                },
                "value": "tali_engelen"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "beagledad360"
                                ],
                                "photo": [
                                    "https://igcdn-photos-b-a.akamaihd.net/hphotos-ak-xaf1/t51.2885-19/11429646_1455219171455961_1729890400_a.jpg"
                                ],
                                "url": [
                                    "http://instagram.com/beagledad360"
                                ]
                            },
                            "value": "beagledad360"
                        }
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#liked-by-1292997922"
                    ],
                    "name": [
                        "beagledad360"
                    ]
                },
                "value": "beagledad360"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "sweetmadicakes"
                                ],
                                "photo": [
                                    "https://igcdn-photos-f-a.akamaihd.net/hphotos-ak-xft1/t51.2885-19/11111381_356776037850837_443483298_a.jpg"
                                ],
                                "url": [
                                    "http://instagram.com/sweetmadicakes"
                                ]
                            },
                            "value": "sweetmadicakes"
                        }
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#liked-by-181128730"
                    ],
                    "name": [
                        "sweetmadicakes"
                    ]
                },
                "value": "sweetmadicakes"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Ankur"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/igcdn-photos-b-a.akamaihd.net/ae05b7dcc1946a0f6c26ff1d293e41122f532dff7d778152b30a4229ac1db68d.jpeg"
                                ],
                                "url": [
                                    "http://instagram.com/strangeways"
                                ]
                            },
                            "value": "Ankur"
                        }
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#liked-by-341817"
                    ],
                    "name": [
                        "Ankur"
                    ]
                },
                "value": "Ankur"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Jason Lander"
                                ],
                                "photo": [
                                    "https://igcdn-photos-g-a.akamaihd.net/hphotos-ak-xfa1/t51.2885-19/11288201_1410303702631798_653411788_a.jpg"
                                ],
                                "url": [
                                    "http://instagram.com/jwlander"
                                ]
                            },
                            "value": "Jason Lander"
                        }
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#liked-by-2630840"
                    ],
                    "name": [
                        "Jason Lander"
                    ]
                },
                "value": "Jason Lander"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Yuriy"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/igcdn-photos-b-a.akamaihd.net/2e0de1795d41cb1db4c6a8c1ac38eccf84ca0cdbd8360e801c81f7b7dad2e94e.jpeg"
                                ],
                                "url": [
                                    "http://instagram.com/html5cat"
                                ]
                            },
                            "value": "Yuriy"
                        }
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#liked-by-3686317"
                    ],
                    "name": [
                        "Yuriy"
                    ]
                },
                "value": "Yuriy"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Davy Stevenson"
                                ],
                                "photo": [
                                    "https://igcdn-photos-c-a.akamaihd.net/hphotos-ak-xfa1/t51.2885-19/11242598_1623738551240954_1317942121_a.jpg"
                                ],
                                "url": [
                                    "http://instagram.com/davystevenson"
                                ]
                            },
                            "value": "Davy Stevenson"
                        }
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#liked-by-4320933"
                    ],
                    "name": [
                        "Davy Stevenson"
                    ]
                },
                "value": "Davy Stevenson"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "Jessica"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/igcdn-photos-f-a.akamaihd.net/9365b4b98378a823e6eb0ae76c0a42fa0a540e96b259b22ef414c516c23f5785.jpeg"
                                ],
                                "url": [
                                    "http://instagram.com/j_lorraine"
                                ]
                            },
                            "value": "Jessica"
                        }
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#liked-by-21872111"
                    ],
                    "name": [
                        "Jessica"
                    ]
                },
                "value": "Jessica"
            },
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "oseidel"
                                ],
                                "photo": [
                                    "https://instagramimages-a.akamaihd.net/profiles/profile_14266744_75sq_1367597664.jpg"
                                ],
                                "url": [
                                    "http://instagram.com/oseidel"
                                ]
                            },
                            "value": "oseidel"
                        }
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#liked-by-14266744"
                    ],
                    "name": [
                        "oseidel"
                    ]
                },
                "value": "oseidel"
            }
        ],
        "comment": [
            {
                "type": [
                    "h-cite"
                ],
                "properties": {
                    "author": [
                        {
                            "type": [
                                "h-card"
                            ],
                            "properties": {
                                "name": [
                                    "lord__burns"
                                ],
                                "photo": [
                                    "https://s3-us-west-2.amazonaws.com/aaronparecki.com/igcdn-photos-h-a.akamaihd.net/d75e0eed375579fc0c83c87b1c232db6528b50512dc492117bba0b62e3a1db8c.jpeg"
                                ],
                                "url": [
                                    "http://instagram.com/lord__burns"
                                ]
                            },
                            "value": "lord__burns"
                        }
                    ],
                    "name": [
                        "This is a great one"
                    ],
                    "url": [
                        "https://instagram.com/p/5bIArCjcsW/#comment-1034549797149592539"
                    ],
                    "published": [
                        "2015-07-21T23:52:48-07:00"
                    ],
                    "content": [
                        {
                            "html": "This is a great one",
                            "value": "This is a great one"
                        }
                    ]
                },
                "value": "This is a great one"
            }
        ],
        "name": [
            "...and down! #pdx #travel"
        ],
        "category": [
            "pdx",
            "travel",
            "travel",
            "pdx"
        ],
        "video": [
            "https://aaronparecki.com/2015/07/21/16/video.mp4"
        ],
        "photo": [
            "https://aaronparecki.com/2015/07/21/16/photo.jpeg"
        ],
        "poster": [
            "https://aaronparecki.com/2015/07/21/16/photo.jpeg"
        ],
        "url": [
            "https://aaronparecki.com/2015/07/21/16/travel-pdx"
        ],
        "syndication": [
            "https://instagram.com/p/5bIArCjcsW/"
        ],
        "author": [
            "https://aaronparecki.com/"
        ],
        "published": [
            "2015-07-21T20:45:59-07:00"
        ],
        "content": [
            {
                "html": "...and down! <a href=\"https://aaronparecki.com/tag/pdx\">#<span class=\"p-category\">pdx</span></a> <a href=\"https://aaronparecki.com/tag/travel\">#<span class=\"p-category\">travel</span></a>",
                "value": "...and down! #pdx #travel"
            }
        ]
    }
}
\ No newline at end of file

A spec/examples/entry/video-shane.json => spec/examples/entry/video-shane.json +71 -0
@@ 0,0 1,71 @@
{
    "type": [
        "h-entry"
    ],
    "properties": {
        "author": [
            {
                "type": [
                    "h-card"
                ],
                "properties": {
                    "name": [
                        "Shane Becker"
                    ],
                    "url": [
                        "https://veganstraightedge.com/",
                        "https://veganstraightedge.com/"
                    ],
                    "photo": [
                        "https://veganstraightedge.com/photo.svg"
                    ]
                },
                "value": "Shane Becker"
            }
        ],
        "name": [
            "Backyard squirrel buddy."
        ],
        "dm-title": [
            "Backyard squirrel buddy."
        ],
        "summary": [
            ""
        ],
        "x-post-type": [
            "video"
        ],
        "category": [
            "imported:from=vine.co"
        ],
        "url": [
            "https://veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy",
            "https://veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy"
        ],
        "uid": [
            "https://veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy",
            "https://veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy"
        ],
        "featured": [
            "http://assets.veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy.png"
        ],
        "video": [
            "http://assets.veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy.mp4"
        ],
        "photo": [
            "http://assets.veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy.png\""
        ],
        "x-enclosure": [
            "http://assets.veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy.mp4"
        ],
        "published": [
            "2013-05-31T10:35:31Z"
        ],
        "content": [
            {
                "html": "\n  <video controls width=\"480\" height=\"480\" poster=\"http://assets.veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy.png\" class=\"u-featured\"><source src=\"http://assets.veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy.mp4\" class=\"u-video\"></source><img src=\"http://assets.veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy.png%22\" class=\"u-photo\"></video><div class=\"p-summary\">\n    <p></p>\n  </div>\n\n  <p class=\"download\">\n    <a class=\"u-x-enclosure\" href=\"http://assets.veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy.mp4\">Download video</a>\n  </p>\n",
                "value": "http://assets.veganstraightedge.com/videos/2013/05/31/backyard-squirrel-buddy.png\"\n     \n   \n\n  \n    Download video"
            }
        ]
    }
}
\ No newline at end of file

R spec/examples/article-aaron.json => spec/examples/items/article-aaron.json +0 -0

R spec/examples/article-tantek.json => spec/examples/items/article-tantek.json +0 -0

R spec/examples/like-aaron.json => spec/examples/items/like-aaron.json +0 -0

R spec/examples/note-aaron.json => spec/examples/items/note-aaron.json +0 -0

R spec/examples/note-aaron2.json => spec/examples/items/note-aaron2.json +0 -0

R spec/examples/photo-aaron.json => spec/examples/items/photo-aaron.json +0 -0

R spec/examples/photo-tantek.json => spec/examples/items/photo-tantek.json +0 -0

R spec/examples/reply-aaron.json => spec/examples/items/reply-aaron.json +0 -0

R spec/examples/reply-aaronmultiple.json => spec/examples/items/reply-aaronmultiple.json +0 -0

R spec/examples/repost-aaron.json => spec/examples/items/repost-aaron.json +0 -0

R spec/examples/rsvp-aaron.json => spec/examples/items/rsvp-aaron.json +0 -0

R spec/examples/rsvp-aaronmultiple.json => spec/examples/items/rsvp-aaronmultiple.json +0 -0

R spec/examples/rsvp-tantek.json => spec/examples/items/rsvp-tantek.json +0 -0

R spec/examples/video-aaron.json => spec/examples/items/video-aaron.json +0 -0

R spec/examples/video-shane.json => spec/examples/items/video-shane.json +0 -0

A spec/examples/jf2/basic-photo-invalid-url.json => spec/examples/jf2/basic-photo-invalid-url.json +8 -0
@@ 0,0 1,8 @@
{
  "type": "entry",
  "photo": "8nrok.jpg",
  "content": {
    "text": "First night in the bayous of Louisiana is pretty special",
    "html":"<p>First night in the bayous of Louisiana is pretty special</p>"
  }
}
\ No newline at end of file

A spec/examples/jf2/basic-photo.json => spec/examples/jf2/basic-photo.json +8 -0
@@ 0,0 1,8 @@
{
  "type": "entry",
  "photo": "https://grant.codes/media/2018/03/22/8nrok.jpg",
  "content": {
    "text": "First night in the bayous of Louisiana is pretty special",
    "html":"<p>First night in the bayous of Louisiana is pretty special</p>"
  }
}
\ No newline at end of file

A spec/examples/jf2/basic-reply.json => spec/examples/jf2/basic-reply.json +5 -0
@@ 0,0 1,5 @@
{
  "type": "entry",
  "in-reply-to": "https://example.com",
  "content": "This is the reply content"
}
\ No newline at end of file

A spec/examples/jf2/html-content.json => spec/examples/jf2/html-content.json +7 -0
@@ 0,0 1,7 @@
{
  "type": "entry",
  "content": {
    "html": "<p>Hello World</p>",
    "text": "Hello World"
  }
}
\ No newline at end of file

A spec/examples/jf2/like.json => spec/examples/jf2/like.json +4 -0
@@ 0,0 1,4 @@
{
  "type": "entry",
  "like-of": "https://example.com"
}
\ No newline at end of file

A spec/examples/jf2/plain-text.json => spec/examples/jf2/plain-text.json +4 -0
@@ 0,0 1,4 @@
{
  "type": "entry",
  "content": "This is just some simple text"
}
\ No newline at end of file

A spec/examples/jf2/repost.json => spec/examples/jf2/repost.json +4 -0
@@ 0,0 1,4 @@
{
  "type": "entry",
  "repost-of": "https://example.com/repost"
}
\ No newline at end of file

A spec/examples/jf2/rsvp.json => spec/examples/jf2/rsvp.json +6 -0
@@ 0,0 1,6 @@
{
  "type": "entry",
  "rsvp": "yes",
  "in-reply-to": "https://event.example.com",
  "content": "I'm going to an event"
}
\ No newline at end of file

A spec/examples/jf2/video.json => spec/examples/jf2/video.json +20 -0
@@ 0,0 1,20 @@
{
  "type": "entry",
  "name": "...and down! #pdx #travel",
  "category": [
      "pdx",
      "travel",
      "travel",
      "pdx"
  ],
  "video": "https://aaronparecki.com/2015/07/21/16/video.mp4",
  "photo": "https://aaronparecki.com/2015/07/21/16/photo.jpeg",
  "poster": "https://aaronparecki.com/2015/07/21/16/photo.jpeg",
  "url": "https://aaronparecki.com/2015/07/21/16/travel-pdx",
  "author": "https://aaronparecki.com/",
  "published": "2015-07-21T20:45:59-07:00",
  "content": {
      "html": "...and down! <a href=\"https://aaronparecki.com/tag/pdx\">#<span class=\"p-category\">pdx</span></a> <a href=\"https://aaronparecki.com/tag/travel\">#<span class=\"p-category\">travel</span></a>",
      "text": "...and down! #pdx #travel"
  }
}
\ No newline at end of file

A spec/indieweb/post_types/configuration_spec.rb => spec/indieweb/post_types/configuration_spec.rb +73 -0
@@ 0,0 1,73 @@
RSpec.shared_examples_for 'a default configuration' do
  context '.logger' do
    it { expect(Indieweb::PostTypes.logger).to be_an_instance_of(Logger) }
    it { expect(Indieweb::PostTypes.logger.progname).to eq('IndiewebPostTypes') }
    it { expect(Indieweb::PostTypes.logger.level).to eq(Logger::WARN) }
  end

  context '.identifiers' do
    it { expect(Indieweb::PostTypes.identifiers).to eq(Indieweb::PostTypes.default_identifiers) }
  end
end

RSpec.describe Indieweb::PostTypes::Configuration do
  context '.configure' do
    context 'default configuration' do
      it_behaves_like 'a default configuration'
    end

    context '.logger' do
      before do
        Indieweb::PostTypes.configure do |config|
          config.logger = 'a logger'
        end
      end

      it { expect(Indieweb::PostTypes.logger).to eq('a logger') }
    end

    context '.identifiers' do
      before do
        Indieweb::PostTypes.configure do |config|
          config.identifiers = ['a', 'b', 'c']
        end
      end

      it { expect(Indieweb::PostTypes.identifiers).to eq(['a', 'b', 'c']) }
    end

    context '.insert_identifier' do
      let(:default_identifiers) { Indieweb::PostTypes.default_identifiers }
      let(:expected_identifiers) do
        default_identifiers[0..-2] +
        ['AnExtraIdentifierClass'] +
        [default_identifiers.last]
      end

      before do
        Indieweb::PostTypes.reset_configuration!
        Indieweb::PostTypes.configure do |config|
          config.insert_identifier(klass: 'AnExtraIdentifierClass',
                                   before: Indieweb::PostTypes.identifiers.last)
        end
      end

      it 'includes the added identifier correctly' do
        expect(Indieweb::PostTypes.identifiers).to eq(expected_identifiers)
      end
    end
  end

  context '.reset_configuration!' do
    before do
      Indieweb::PostTypes.reset_configuration!
      Indieweb::PostTypes.configure do |config|
        config.insert_identifier(klass: 'AnExtraIdentifierClass',
                                 before: Indieweb::PostTypes.identifiers.last)
      end
      Indieweb::PostTypes.reset_configuration!
    end

    it_behaves_like 'a default configuration'
  end
end

M spec/indieweb/post_types/identifier/article_spec.rb => spec/indieweb/post_types/identifier/article_spec.rb +4 -11
@@ 1,13 1,9 @@
RSpec.describe Indieweb::PostTypes::Identifier::Article do
  context '.type_from' do
    context 'when the identifier should identify the type' do
      %w[
        article-aaron.json
        article-aaron.json
        article-tantek.json
      ].each do |filename|
      examples_for(:article).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to eq('article') }
        end


@@ 15,12 11,9 @@ RSpec.describe Indieweb::PostTypes::Identifier::Article do
    end

    context 'when the identifier should not identify the type' do
      %w[
        note-aaron.json
        note-aaron2.json
      ].each do |filename|
      examples_for(:note).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to be_nil }
        end

M spec/indieweb/post_types/identifier/like_spec.rb => spec/indieweb/post_types/identifier/like_spec.rb +24 -16
@@ 1,11 1,9 @@
RSpec.describe Indieweb::PostTypes::Identifier::Like do
  context '.type_from' do
    context 'when the identifier should identify the type' do
      %w[
        like-aaron.json
      ].each do |filename|
      examples_for(:like).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to eq('like') }
        end


@@ 13,19 11,29 @@ RSpec.describe Indieweb::PostTypes::Identifier::Like do
    end

    context 'when the identifier should not identify the type' do
      %w[
        video-aaron.json
        video-shane.json
        photo-aaron.json
        photo-tantek.json
        article-aaron.json
        article-aaron.json
        article-tantek.json
        note-aaron.json
        note-aaron2.json
      ].each do |filename|
      examples_for(:video, :photo, :article, :note).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to be_nil }
        end
      end

      context 'due to the url not being acceptable' do
        let(:data) do
          json_data_for('entry/like-aaron.json').tap do |data|
            data['properties']['like-of'] = url
          end
        end

        context 'because it is not valid' do
          let(:url) { 'https://foo bar.com/' }

          it { expect(described_class.type_from(data)).to be_nil }
        end

        context 'due to the url not being http or https' do
          let(:url) { 'ftp://example.com/' }

          it { expect(described_class.type_from(data)).to be_nil }
        end

M spec/indieweb/post_types/identifier/note_spec.rb => spec/indieweb/post_types/identifier/note_spec.rb +2 -19
@@ 1,26 1,9 @@
RSpec.describe Indieweb::PostTypes::Identifier::Note do
  context '.type_from' do
    context 'when the identifier should identify the type' do
      %w[
        rsvp-aaron.json
        rsvp-aaronmultiple.json
        rsvp-tantek.json
        reply-aaron.json
        reply-aaronmultiple.json
        repost-aaron.json
        like-aaron.json
        video-aaron.json
        video-shane.json
        photo-aaron.json
        photo-tantek.json
        article-aaron.json
        article-aaron.json
        article-tantek.json
        note-aaron.json
        note-aaron2.json
      ].each do |filename|
      examples_for(:rsvp, :reply, :repost, :like, :video, :photo, :article, :note).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to eq('note') }
        end

M spec/indieweb/post_types/identifier/photo_spec.rb => spec/indieweb/post_types/identifier/photo_spec.rb +24 -13
@@ 1,12 1,9 @@
RSpec.describe Indieweb::PostTypes::Identifier::Photo do
  context '.type_from' do
    context 'when the identifier should identify the type' do
      %w[
        photo-aaron.json
        photo-tantek.json
      ].each do |filename|
      examples_for(:photo).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to eq('photo') }
        end


@@ 14,15 11,29 @@ RSpec.describe Indieweb::PostTypes::Identifier::Photo do
    end

    context 'when the identifier should not identify the type' do
      %w[
        article-aaron.json
        article-aaron.json
        article-tantek.json
        note-aaron.json
        note-aaron2.json
      ].each do |filename|
      examples_for(:article, :note).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to be_nil }
        end
      end

      context 'due to the url not being acceptable' do
        let(:data) do
          json_data_for('entry/photo-tantek.json').tap do |data|
            data['properties']['photo'] = url
          end
        end

        context 'because it is not valid' do
          let(:url) { 'https://foo bar.com/' }

          it { expect(described_class.type_from(data)).to be_nil }
        end

        context 'due to the url not being http or https' do
          let(:url) { 'ftp://example.com/' }

          it { expect(described_class.type_from(data)).to be_nil }
        end

M spec/indieweb/post_types/identifier/reply_spec.rb => spec/indieweb/post_types/identifier/reply_spec.rb +24 -19
@@ 1,12 1,9 @@
RSpec.describe Indieweb::PostTypes::Identifier::Reply do
  context '.type_from' do
    context 'when the identifier should identify the type' do
      %w[
        reply-aaron.json
        reply-aaronmultiple.json
      ].each do |filename|
      examples_for(:reply).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to eq('reply') }
        end


@@ 14,21 11,29 @@ RSpec.describe Indieweb::PostTypes::Identifier::Reply do
    end

    context 'when the identifier should not identify the type' do
      %w[
        repost-aaron.json
        like-aaron.json
        video-aaron.json
        video-shane.json
        photo-aaron.json
        photo-tantek.json
        article-aaron.json
        article-aaron.json
        article-tantek.json
        note-aaron.json
        note-aaron2.json
      ].each do |filename|
      examples_for(:repost, :like, :video, :photo, :article, :note).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to be_nil }
        end
      end

      context 'due to the url not being acceptable' do
        let(:data) do
          json_data_for('entry/reply-aaron.json').tap do |data|
            data['properties']['in-reply-to'] = url
          end
        end

        context 'because it is not valid' do
          let(:url) { 'https://foo bar.com/' }

          it { expect(described_class.type_from(data)).to be_nil }
        end

        context 'due to the url not being http or https' do
          let(:url) { 'ftp://example.com/' }

          it { expect(described_class.type_from(data)).to be_nil }
        end

M spec/indieweb/post_types/identifier/repost_spec.rb => spec/indieweb/post_types/identifier/repost_spec.rb +24 -17
@@ 1,11 1,9 @@
RSpec.describe Indieweb::PostTypes::Identifier::Repost do
  context '.type_from' do
    context 'when the identifier should identify the type' do
      %w[
        repost-aaron.json
      ].each do |filename|
      examples_for(:repost).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to eq('repost') }
        end


@@ 13,20 11,29 @@ RSpec.describe Indieweb::PostTypes::Identifier::Repost do
    end

    context 'when the identifier should not identify the type' do
      %w[
        like-aaron.json
        video-aaron.json
        video-shane.json
        photo-aaron.json
        photo-tantek.json
        article-aaron.json
        article-aaron.json
        article-tantek.json
        note-aaron.json
        note-aaron2.json
      ].each do |filename|
      examples_for(:like, :video, :photo, :article, :note).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to be_nil }
        end
      end

      context 'due to the url not being acceptable' do
        let(:data) do
          json_data_for('jf2/repost.json').tap do |data|
            data['repost-of'] = url
          end
        end

        context 'because it is not valid' do
          let(:url) { 'https://foo bar.com/' }

          it { expect(described_class.type_from(data)).to be_nil }
        end

        context 'due to the url not being http or https' do
          let(:url) { 'ftp://example.com/' }

          it { expect(described_class.type_from(data)).to be_nil }
        end

M spec/indieweb/post_types/identifier/rsvp_spec.rb => spec/indieweb/post_types/identifier/rsvp_spec.rb +11 -29
@@ 1,41 1,23 @@
RSpec.describe Indieweb::PostTypes::Identifier::Rsvp do
  context '.type_from' do
    context 'when the identifier should identify the type' do
      %w[
        rsvp-aaron.json
        rsvp-aaronmultiple.json
        rsvp-tantek.json
      ].each do |filename|
      examples_for(:rsvp).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to eq('rsvp') }
        end
      end
    end

    # context 'when the identifier should not identify the type' do
    #   %w(
    #     reply-aaron.json
    #     reply-aaronmultiple.json
    #     repost-aaron.json
    #     like-aaron.json
    #     video-aaron.json
    #     video-shane.json
    #     photo-aaron.json
    #     photo-tantek.json
    #     article-aaron.json
    #     article-aaron.json
    #     article-tantek.json
    #     note-aaron.json
    #     note-aaron2.json
    #   ).each do |filename|
    #     context "when the data is from #{filename}" do
    #       let(:data) { example_data_for(filename) }
    #
    #       it { expect(described_class.type_from(data)).to be_nil }
    #     end
    #   end
    # end
    context 'when the identifier should not identify the type' do
      examples_for(:reply, :repost, :like, :video, :photo, :article, :note).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to be_nil }
        end
      end
    end
  end
end

M spec/indieweb/post_types/identifier/video_spec.rb => spec/indieweb/post_types/identifier/video_spec.rb +24 -15
@@ 1,12 1,9 @@
RSpec.describe Indieweb::PostTypes::Identifier::Video do
  context '.type_from' do
    context 'when the identifier should identify the type' do
      %w[
        video-aaron.json
        video-shane.json
      ].each do |filename|
      examples_for(:video).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to eq('video') }
        end


@@ 14,17 11,29 @@ RSpec.describe Indieweb::PostTypes::Identifier::Video do
    end

    context 'when the identifier should not identify the type' do
      %w[
        photo-aaron.json
        photo-tantek.json
        article-aaron.json
        article-aaron.json
        article-tantek.json
        note-aaron.json
        note-aaron2.json
      ].each do |filename|
      examples_for(:photo, :article, :note).each do |filename|
        context "when the data is from #{filename}" do
          let(:data) { example_data_for(filename) }
          let(:data) { json_data_for(filename) }

          it { expect(described_class.type_from(data)).to be_nil }
        end
      end

      context 'due to the url not being acceptable' do
        let(:data) do
          json_data_for('jf2/video.json').tap do |data|
            data['video'] = url
          end
        end

        context 'because it is not valid' do
          let(:url) { 'https://foo bar.com/' }

          it { expect(described_class.type_from(data)).to be_nil }
        end

        context 'due to the url not being http or https' do
          let(:url) { 'ftp://example.com/' }

          it { expect(described_class.type_from(data)).to be_nil }
        end

M spec/indieweb/post_types_spec.rb => spec/indieweb/post_types_spec.rb +12 -40
@@ 2,31 2,19 @@ RSpec.describe Indieweb::PostTypes do
  let(:default_identifiers) { described_class.default_identifiers }

  context 'with the default identifiers' do
    before { described_class.reset_configuration! }

    it 'lists the identifiers correctly' do
      expect(described_class.identifiers).to eq(default_identifiers)
    end

    {
      'rsvp-aaron.json' => 'rsvp',
      'rsvp-aaronmultiple.json' => 'rsvp',
      'rsvp-tantek.json' => 'rsvp',
      'reply-aaron.json' => 'reply',
      'reply-aaronmultiple.json' => 'reply',
      'repost-aaron.json' => 'repost',
      'like-aaron.json' => 'like',
      'video-aaron.json' => 'video',
      'video-shane.json' => 'video',
      'photo-aaron.json' => 'photo',
      'photo-tantek.json' => 'photo',
      'article-aaron.json' => 'article',
      'article-tantek.json' => 'article',
      'note-aaron.json' => 'note',
      'note-aaron2.json' => 'note'
    }.each_pair do |filename, expected_type|
      context "when the file is #{filename}" do
        let(:data) { example_data_for(filename) }
    %i[rsvp reply repost like video photo article note].each do |post_type|
      examples_for(post_type).each do |filename|
        context "when the file is #{filename}" do
          let(:data) { json_data_for(filename) }

        it { expect(described_class.type_from(data)).to eq(expected_type) }
          it { expect(described_class.type_from(data)).to eq(post_type.to_s) }
        end
      end
    end
  end


@@ 47,7 35,8 @@ RSpec.describe Indieweb::PostTypes do
    end

    before do
      Indieweb::PostTypes.configure do |config|
      described_class.reset_configuration!
      described_class.configure do |config|
        config.insert_identifier(klass: CatchAllIdentifier,
                                 before: described_class.identifiers.first)
      end


@@ 57,26 46,9 @@ RSpec.describe Indieweb::PostTypes do
      expect(described_class.identifiers).to eq(expected_identifiers)
    end

    [
      'rsvp-aaron.json',
      'rsvp-aaronmultiple.json',
      'rsvp-tantek.json',
      'reply-aaron.json',
      'reply-aaronmultiple.json',
      'repost-aaron.json',
      'like-aaron.json',
      'video-aaron.json',
      'video-shane.json',
      'photo-aaron.json',
      'photo-tantek.json',
      'article-aaron.json',
      'article-aaron.json',
      'article-tantek.json',
      'note-aaron.json',
      'note-aaron2.json'
    ].each do |filename|
    examples_for(:rsvp, :reply, :repost, :like, :video, :photo, :article, :note).each do |filename|
      context "when the file is #{filename}" do
        let(:data) { example_data_for(filename) }
        let(:data) { json_data_for(filename) }

        it { expect(described_class.type_from(data)).to eq('catchall') }
      end

M spec/spec_helper.rb => spec/spec_helper.rb +6 -1
@@ 1,3 1,7 @@
require 'simplecov'

SimpleCov.start

require 'bundler/setup'
require 'indieweb/post_types'
require 'json'


@@ 17,5 21,6 @@ RSpec.configure do |config|
    c.syntax = :expect
  end

  config.include ExampleData
  config.extend ExampleData
  config.include JsonFor
end

M spec/support/example_data.rb => spec/support/example_data.rb +99 -3
@@ 1,6 1,102 @@
module ExampleData
  def example_data_for(filename)
    file = File.join(File.dirname(__FILE__), '../examples', filename)
    JSON.parse(File.read(file))
  def examples_for(*types)
    grouped_examples.values_at(*types).flatten.sort
  end

  private

  def grouped_examples
    {
      rsvp: rsvp_examples,
      reply: reply_examples,
      repost: repost_examples,
      like: like_examples,
      video: video_examples,
      photo: photo_examples,
      article: article_examples,
      note: note_examples
    }
  end

  def rsvp_examples
    %w[
      entry/rsvp-aaron.json
      entry/rsvp-aaronmultiple.json
      entry/rsvp-tantek.json
      items/rsvp-aaron.json
      items/rsvp-aaronmultiple.json
      items/rsvp-tantek.json
      jf2/rsvp.json
    ]
  end

  def reply_examples
    %w[
      entry/reply-aaron.json
      entry/reply-aaronmultiple.json
      items/reply-aaron.json
      items/reply-aaronmultiple.json
      jf2/basic-reply.json
    ]
  end

  def repost_examples
    %w[
      entry/repost-aaron.json
      items/repost-aaron.json
      jf2/repost.json
    ]
  end

  def like_examples
    %w[
      entry/like-aaron.json
      entry/like-barryf.json
      items/like-aaron.json
      jf2/like.json
    ]
  end

  def video_examples
    %w[
      entry/video-aaron.json
      entry/video-shane.json
      items/video-aaron.json
      items/video-shane.json
      jf2/video.json
    ]
  end

  def photo_examples
    %w[
      entry/photo-aaron.json
      entry/photo-tantek.json
      items/photo-aaron.json
      items/photo-tantek.json
      jf2/basic-photo.json
    ]
  end

  def article_examples
    %w[
      entry/article-aaron.json
      entry/article-barryf.json
      entry/article-tantek.json
      items/article-aaron.json
      items/article-tantek.json
    ]
  end

  def note_examples
    %w[
      entry/note-aaron.json
      entry/note-aaron2.json
      entry/note-micropub-rocks.json
      items/note-aaron.json
      items/note-aaron2.json
      jf2/basic-photo-invalid-url.json
      jf2/html-content.json
      jf2/plain-text.json
    ]
  end
end

A spec/support/json_for.rb => spec/support/json_for.rb +6 -0
@@ 0,0 1,6 @@
module JsonFor
  def json_data_for(filename)
    file = File.join(File.dirname(__FILE__), '../examples', filename)
    JSON.parse(File.read(file))
  end
end