tributary 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +6 -0
  3. data/LICENCE +661 -0
  4. data/README.md +99 -0
  5. data/Rakefile +5 -0
  6. data/config.ru +9 -0
  7. data/lib/tributary/app.rb +61 -0
  8. data/lib/tributary/item.rb +66 -0
  9. data/lib/tributary/plugins/dummy.rb +7 -0
  10. data/lib/tributary/plugins/mnml.rb +14 -0
  11. data/lib/tributary/plugins/unbreak_my_art.rb +22 -0
  12. data/lib/tributary/stream.rb +51 -0
  13. data/lib/tributary.rb +8 -0
  14. data/spec/fixtures/index.en.en+pl.xml +50 -0
  15. data/spec/fixtures/index.en.en.xml +50 -0
  16. data/spec/fixtures/index.en.xml +50 -0
  17. data/spec/fixtures/index.pl.pl.xml +50 -0
  18. data/spec/fixtures/index.pl.xml +50 -0
  19. data/spec/fixtures/index.xml +50 -0
  20. data/spec/fixtures/layout.css +22 -0
  21. data/spec/fixtures/pages.css +2 -0
  22. data/spec/site/articles/600.en.md +5 -0
  23. data/spec/site/articles/600.md +5 -0
  24. data/spec/site/articles/bilingual.en.md +4 -0
  25. data/spec/site/articles/bilingual.pl.md +4 -0
  26. data/spec/site/articles/unix-millennium-bug.en.md +4 -0
  27. data/spec/site/articles/welcome.md +4 -0
  28. data/spec/site/beeps/beep.md +3 -0
  29. data/spec/site/beeps/dated.md +3 -0
  30. data/spec/site/beeps/english.en.md +3 -0
  31. data/spec/site/beeps/link.md +3 -0
  32. data/spec/site/beeps/polish.pl.md +3 -0
  33. data/spec/site/beeps/quote.md +4 -0
  34. data/spec/site/i18n/en.yml +12 -0
  35. data/spec/site/i18n/pl.yml +12 -0
  36. data/spec/site/pages/about.md +5 -0
  37. data/spec/site/views/articles.haml +4 -0
  38. data/spec/site/views/articles.index.haml +6 -0
  39. data/spec/site/views/articles.index.sass +0 -0
  40. data/spec/site/views/articles.sass +2 -0
  41. data/spec/site/views/beeps.haml +1 -0
  42. data/spec/site/views/beeps.sass +2 -0
  43. data/spec/site/views/error.haml +8 -0
  44. data/spec/site/views/error.sass +6 -0
  45. data/spec/site/views/index.haml +6 -0
  46. data/spec/site/views/index.sass +2 -0
  47. data/spec/site/views/index.xml.haml +17 -0
  48. data/spec/site/views/layout.haml +56 -0
  49. data/spec/site/views/layout.sass +22 -0
  50. data/spec/site/views/pages.haml +3 -0
  51. data/spec/site/views/pages.sass +2 -0
  52. data/spec/tributary/app_spec.rb +266 -0
  53. data/spec/tributary/item_spec.rb +129 -0
  54. data/spec/tributary/plugins/dummy_spec.rb +12 -0
  55. data/spec/tributary/plugins/mnml_spec.rb +16 -0
  56. data/spec/tributary/plugins/unbreak_my_art_spec.rb +36 -0
  57. data/spec/tributary/stream_spec.rb +155 -0
  58. data/tributary.gemspec +18 -0
  59. metadata +204 -0
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ tributary
2
+ =========
3
+
4
+ A tiny, heavily [toto](http://cloudhead.io/toto)-inspired and (no less heavily) [Sinatra](http://www.sinatrarb.com/)-powered blogging engine.
5
+
6
+
7
+
8
+ Overview
9
+ --------
10
+
11
+ tributary aggregates a set of `Item`s, representing individual website contents (blog posts, photolog entries, standalone pages, etc.) stored in YAML-enhanced Markdown files, and allows accessing them with a simple 1:1 URL-to-filename mapping. All `Item`s with a publication date are combined into a `Stream`, which can be interated over (to, e.g., display the seven most recent `Item`s, or all photos) or navigated from inside (to get the `Item` that’s previous/subsequent to the current one).
12
+
13
+
14
+
15
+ Items
16
+ -----
17
+
18
+ An `Item` is represented in the filesystem in the form of text file with a Markdown body and a YAML header:
19
+
20
+ date: 2010-07-15
21
+ title: welcome to tributary
22
+
23
+ tributary _welcome_ article
24
+
25
+ The above example, if stored as `articles/welcome.md`, will be seen in tributary as an `Item` object with `Item#body` returning the kramdown-generated `<p>tributary <em>welcome</em> article</p>\n`, `Item#title` and `Item#date` returning YAML-parsed `welcome to tributary` and `2010-07-15 00:00:00 +0200`, while `Item#type` and `Item#path` will be inherited from the filesystem location and return `:articles` and `welcome`, respectively.
26
+
27
+
28
+
29
+ Item types
30
+ ----------
31
+
32
+ Every `Item` has a type, inherited from the `Item`’s position in the filesystem and returned by `Item#type`. When a given `Item` is requested over HTTP (via its `Item#path`) the view associated with its type is rendered; this allows rendering different HTML/CSS layouts for different content types (‘static’ pages vs blog posts vs photolog entries, for example).
33
+
34
+
35
+
36
+ Views
37
+ -----
38
+
39
+ As mentioned above, every `Item` has an associated type, and this type defines the HTML view displayed when the given `Item` is requested; this allows for different rendering of e.g. static pages vs photolog entries. The views are written in [Haml](http://haml-lang.com/) and are located in `views/*.haml` files, with the file’s basename equal to the `Item`’s type.
40
+
41
+ Additionally, for every type the URL with that type’s name can be accessed (for example `http://…/pages`, assuming at least in one case `Item#type` returns `:pages`). In this case, the relevant `views/*.index.haml` Haml view is rendered (so `views/pages.index.haml` in the aforementioned example). These views can be used to create custom category-like pages – e.g., a page indexing all of your photographs that renders their thumbnails.
42
+
43
+
44
+
45
+ Stream
46
+ ------
47
+
48
+ The `Stream` object contains all tributary `Item`s ordered by their publication date (if present) and can be queried to return a given number of the most recent `Item`s or an `Item` previous of (or subsequent to) a given `Item` (the returned `Item`(s) take into account the current `locale` and `lang_limit` settings). Every such query can additionally filter the `Stream` and for example request the subsequent `Item` that is also a photolog entry.
49
+
50
+
51
+
52
+ Multilingual support
53
+ --------------------
54
+
55
+ There are two session variables governing the user’s language preferences: `locale` and `lang_limit`. The first sets the user’s preferred language (and, for example, allows for a localised user interface), while the second limits the contents returned from the `Stream`.
56
+
57
+ Every `Item` can have multiple language versions (stored in `<type>/<path>.*.md` files, where `*` maps to the relevant locale) and/or a language-agnostic version (stored in the `<type>/<path>.md` file). When a given `Item` is requested (via the `http://…/<path>` URL) tributary chooses the language version most suitable for the request, based on either an explicit `locale` cookie sent along with the request or the `Accept-Language` HTTP header. If the preferred language version is not available, tributary falls back to the language-agnostic version or (if there’s no such version) to a language version that’s not explicitely filtered out by the `lang_limit` setting.
58
+
59
+ The site’s interface can be multilingualised using [R18n](http://r18n.rubyforge.org/) (via [R18n for Sinatra](http://r18n.rubyforge.org/sinatra.html)). The `t` object (available in views) can be sent messages like `t.recent_items`, which are translated to the relevant localised strings based on YAML entries in `i18n/*.yml` files (where `*` maps to the current user’s `locale`). The example `i18n/en.yml` file (in the `spec/site` directory) contains
60
+
61
+ recent_items: recent items
62
+
63
+ while the example `i18n/pl.yml` file contains
64
+
65
+ recent_items: najnowsze
66
+
67
+ – and so the `views/index.haml` view used by the spec site can call `t.recent_items` to get a properly localised string (note that `Tributary::App` already registers `Sinatra::R18n` and exposes `locale` to the session, while also setting the `locale` to either the user’s preference, their browser configuration’s default or English, so there’s nothing more that needs to be done).
68
+
69
+
70
+
71
+ Configuration
72
+ -------------
73
+
74
+ Application-level configuration is stored right on the `App` object itself; see the example `config.ru`, which happens to serve the site used by specs:
75
+
76
+ Tributary::App.configure do |config|
77
+ config.set :author, 'Ary Tribut'
78
+ config.set :root, 'spec/site'
79
+ config.set :sitename, 'a tributary site'
80
+ end
81
+
82
+ User-level configuration is also stored on the `App` object and can be operated on by visiting the `/set?option=value` URLs – for example, setting the `locale` to English and `lang_limit` to English and Polish can be done by visiting the `/set?locale=en&lang_limit=en+pl` URL.
83
+
84
+ The `user_prefs` config option contains a list of settings that can be altered by visiting `/set` (and defaults to `[:lang_limit, :locale]`) – changing this option (by setting it as above in `config.ru`, for example) allows the users to alter other (e.g., nonexistent by default) settings and see the changes reflected on the `App` object.
85
+
86
+ The settings altered by the user are kept in the given user’s session and so persist between requests and visits.
87
+
88
+
89
+
90
+ Plugins
91
+ -------
92
+
93
+ Plugins (put in the `App.plugins` `Array`) are objects which can be sent a `handle` method with an `Item` as a parameter and are expected to return the `Item` (so the calls to subsequent plugins are chainable). See the `Mnml` plugin for an example implementation utilising a `SimpleDelegator` to filter the given `Item`’s `body` and `title` methods.
94
+
95
+
96
+
97
+ ---
98
+
99
+ © MMX Piotr Szotkowski <chastell@chastell.net>, licensed under AGPL 3 (see LICENCE)
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ ENV['TZ'] = 'Europe/Warsaw'
4
+
5
+ RSpec::Core::RakeTask.new :spec
data/config.ru ADDED
@@ -0,0 +1,9 @@
1
+ require './lib/tributary'
2
+
3
+ Tributary::App.configure do |config|
4
+ config.set :author, 'Ary Tribut'
5
+ config.set :root, 'spec/site'
6
+ config.set :sitename, 'a tributary site'
7
+ end
8
+
9
+ run Tributary::App
@@ -0,0 +1,61 @@
1
+ module Tributary class App < Sinatra::Base
2
+
3
+ register Sinatra::R18n
4
+
5
+ use Rack::Session::Cookie, expire_after: 60 * 60 * 24 * 365 * 7
6
+
7
+ def self.configure *args, &block
8
+ set :cache?, production?
9
+ set :lang_limit, nil
10
+ set :locale, nil
11
+ set :plugins, []
12
+ set :user_prefs, [:lang_limit, :locale]
13
+ super
14
+ set :stream, cache? ? Tributary::Stream.new : nil
15
+ end
16
+
17
+ before do
18
+ Tributary::App.locale = session[:locale]
19
+ Tributary::App.lang_limit = session[:lang_limit] && session[:lang_limit].split
20
+ @stream = Tributary::App.stream || Tributary::Stream.new
21
+ end
22
+
23
+ get '/' do
24
+ @item = OpenStruct.new type: :index
25
+ haml @item.type
26
+ end
27
+
28
+ get '/set' do
29
+ params.each { |key, value| session[key.to_sym] = value if App.user_prefs.map(&:to_s).include? key }
30
+ redirect request.referer
31
+ end
32
+
33
+ get '/:feed.xml' do |feed|
34
+ content_type 'application/atom+xml'
35
+ feed, App.locale, lang_limit = feed.split '.'
36
+ App.lang_limit = lang_limit.split if lang_limit
37
+ File.exists?("#{App.views}/#{feed}.xml.haml") ? haml("#{feed}.xml".to_sym, layout: false) : 404
38
+ end
39
+
40
+ get '/:style.css' do |style|
41
+ content_type 'text/css'
42
+ File.exists?("#{App.views}/#{style}.sass") ? sass(style.to_sym) : 404
43
+ end
44
+
45
+ get '/:path' do |path|
46
+ if @stream.types.map(&:to_s).include? path
47
+ @item = OpenStruct.new path: path, type: "#{path}.index".to_sym
48
+ else
49
+ @item = @stream.pick_item path
50
+ end
51
+ @item ? haml(@item.type) : 404
52
+ end
53
+
54
+ error 400...600 do
55
+ unless response.content_type
56
+ @item = OpenStruct.new type: :error
57
+ haml @item.type
58
+ end
59
+ end
60
+
61
+ end end
@@ -0,0 +1,66 @@
1
+ # encoding: UTF-8
2
+
3
+ module Tributary class Item < OpenStruct
4
+
5
+ def initialize file
6
+ @file = file
7
+ yaml, @body = File.read(@file).split "\n\n", 2
8
+ super YAML.load yaml
9
+ end
10
+
11
+ def <=> other
12
+ case
13
+ when other.date && date then other.date <=> date
14
+ when date then -1
15
+ when other.date then 1
16
+ else 0
17
+ end.nonzero? or
18
+ (other.path <=> path).nonzero? or
19
+ case
20
+ when lang == App.locale then -1
21
+ when other.lang == App.locale then 1
22
+ when lang.nil? then -1
23
+ when other.lang.nil? then 1
24
+ else lang <=> other.lang
25
+ end
26
+ end
27
+
28
+ def body
29
+ Kramdown::Document.new(@body).to_html
30
+ end
31
+
32
+ def date
33
+ @date ||= case @table[:date]
34
+ when Date then @table[:date].to_time
35
+ when String then Time.parse @table[:date]
36
+ when Time then @table[:date]
37
+ end
38
+ end
39
+
40
+ alias eql? ==
41
+
42
+ def hash
43
+ @file.hash
44
+ end
45
+
46
+ def lang
47
+ File.basename(@file, '.md').split('.')[1]
48
+ end
49
+
50
+ def path
51
+ @path ||= File.basename(@file, '.md').split('.').first
52
+ end
53
+
54
+ def published?
55
+ date and date < Time.now
56
+ end
57
+
58
+ def title
59
+ @table[:title] or @body.scan(/\p{L}+/).first + '…'
60
+ end
61
+
62
+ def type
63
+ File.dirname(@file).split('/').last.to_sym
64
+ end
65
+
66
+ end end
@@ -0,0 +1,7 @@
1
+ module Tributary module Plugins class Dummy
2
+
3
+ def handle item
4
+ item
5
+ end
6
+
7
+ end end end
@@ -0,0 +1,14 @@
1
+ module Tributary module Plugins class Mnml
2
+
3
+ def handle item
4
+ mnml = SimpleDelegator.new item
5
+ def mnml.body
6
+ super.tr 'aeiouy', ''
7
+ end
8
+ def mnml.title
9
+ super.tr 'aeiouy', ''
10
+ end
11
+ mnml
12
+ end
13
+
14
+ end end end
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+
3
+ module Tributary module Plugins class UnbreakMyArt
4
+
5
+ def handle item
6
+ unbroken = SimpleDelegator.new item
7
+ def unbroken.body
8
+ Plugins::UnbreakMyArt.unbreak super
9
+ end
10
+ def unbroken.title
11
+ Plugins::UnbreakMyArt.unbreak super
12
+ end
13
+ unbroken
14
+ end
15
+
16
+ private
17
+
18
+ def self.unbreak string
19
+ string.gsub /((^|[^\p{L}<])\p{L}\p{P}?) /, '\1 '
20
+ end
21
+
22
+ end end end
@@ -0,0 +1,51 @@
1
+ module Tributary class Stream
2
+
3
+ def initialize root = App.root, plugins = App.plugins
4
+ @items = Dir["#{root}/*/*.md"].map { |file| Item.new file }
5
+ plugins.each do |plugin|
6
+ @items.map! { |item| plugin.handle item }
7
+ end
8
+ end
9
+
10
+ def langs
11
+ @items.map(&:lang).uniq.compact.sort
12
+ end
13
+
14
+ def pick_item path
15
+ path, lang = path.split '.'
16
+ (lang ? @items.select { |item| item.lang == lang } : @items).sort.find { |item| item.path == path }
17
+ end
18
+
19
+ def previous item, filter = {}
20
+ published(filter)[published(filter).index { |i| i.path == item.path } + 1] rescue nil
21
+ end
22
+
23
+ def recent limit = nil, filter = {}
24
+ published(filter).take limit || @items.size
25
+ end
26
+
27
+ def subsequent item, filter = {}
28
+ published(filter).reverse[published(filter).reverse.index { |i| i.path == item.path } + 1] rescue nil
29
+ end
30
+
31
+ def types
32
+ @items.map(&:type).uniq.sort
33
+ end
34
+
35
+ private
36
+
37
+ def items_ltd filter = {}
38
+ items_ltd = @items.sort
39
+ items_ltd.delete_if { |item| item.lang and not App.lang_limit.include? item.lang } if App.lang_limit and not App.lang_limit.empty?
40
+ filter.each do |method, value|
41
+ items_ltd = items_ltd.select { |item| item.send(method) == value }
42
+ end
43
+ items_ltd.select { |item| item == items_ltd.find { |i| i.path == item.path } }
44
+ end
45
+
46
+ def published filter = {}
47
+ @published ||= {}
48
+ @published[[App.lang_limit, App.locale, filter]] ||= items_ltd({published?: true}.merge filter)
49
+ end
50
+
51
+ end end
data/lib/tributary.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'kramdown'
2
+ require 'sinatra/base'
3
+ require 'sinatra/r18n'
4
+ require 'yaml'
5
+
6
+ require_relative 'tributary/app'
7
+ require_relative 'tributary/item'
8
+ require_relative 'tributary/stream'
@@ -0,0 +1,50 @@
1
+ <?xml version='1.0' encoding='utf-8' ?>
2
+ <feed xmlns='http://www.w3.org/2005/Atom'>
3
+ <author>
4
+ <name>Ary Tribut</name>
5
+ </author>
6
+ <generator uri='http://github.com/chastell/tributary'>tributary</generator>
7
+ <id>http://example.org/</id>
8
+ <link href='http://example.org/' rel='alternate' />
9
+ <link href='http://example.org/index.en.en+pl.xml' rel='self' />
10
+ <title>a tributary site</title>
11
+ <updated>2010-07-23T02:00:00+02:00</updated>
12
+ <entry>
13
+ <id>http://example.org/polish</id>
14
+ <link href='http://example.org/polish' rel='alternate' />
15
+ <title>ten…</title>
16
+ <updated>2010-07-23T02:00:00+02:00</updated>
17
+ <content type='html'>&lt;p&gt;ten beep jest tylko po polsku.&lt;/p&gt;</content>
18
+ </entry>
19
+ <entry>
20
+ <id>http://example.org/english</id>
21
+ <link href='http://example.org/english' rel='alternate' />
22
+ <title>this…</title>
23
+ <updated>2010-07-23T01:00:00+02:00</updated>
24
+ <content type='html'>&lt;p&gt;this beep is only in English.&lt;/p&gt;</content>
25
+ </entry>
26
+ <entry>
27
+ <id>http://example.org/600</id>
28
+ <link href='http://example.org/600' rel='alternate' />
29
+ <title>600th anniversary (English)</title>
30
+ <updated>2010-07-15T12:00:00+02:00</updated>
31
+ <content type='html'>
32
+ &lt;p&gt;600 years ago &lt;a href=&quot;http://en.wikipedia.org/wiki/Battle_of_Grunwald&quot;&gt;something supposedly important&lt;/a&gt;
33
+ happened around these parts of the world&lt;/p&gt;
34
+ </content>
35
+ </entry>
36
+ <entry>
37
+ <id>http://example.org/bilingual</id>
38
+ <link href='http://example.org/bilingual' rel='alternate' />
39
+ <title>bilinguality</title>
40
+ <updated>2010-07-15T06:00:00+02:00</updated>
41
+ <content type='html'>&lt;p&gt;this article is also available &lt;a href=&quot;bilingual.pl&quot;&gt;in Polish&lt;/a&gt;&lt;/p&gt;</content>
42
+ </entry>
43
+ <entry>
44
+ <id>http://example.org/dated</id>
45
+ <link href='http://example.org/dated' rel='alternate' />
46
+ <title>a…</title>
47
+ <updated>2010-07-15T03:00:00+02:00</updated>
48
+ <content type='html'>&lt;p&gt;a dated beep.&lt;/p&gt;</content>
49
+ </entry>
50
+ </feed>
@@ -0,0 +1,50 @@
1
+ <?xml version='1.0' encoding='utf-8' ?>
2
+ <feed xmlns='http://www.w3.org/2005/Atom'>
3
+ <author>
4
+ <name>Ary Tribut</name>
5
+ </author>
6
+ <generator uri='http://github.com/chastell/tributary'>tributary</generator>
7
+ <id>http://example.org/</id>
8
+ <link href='http://example.org/' rel='alternate' />
9
+ <link href='http://example.org/index.en.en.xml' rel='self' />
10
+ <title>a tributary site</title>
11
+ <updated>2010-07-23T01:00:00+02:00</updated>
12
+ <entry>
13
+ <id>http://example.org/english</id>
14
+ <link href='http://example.org/english' rel='alternate' />
15
+ <title>this…</title>
16
+ <updated>2010-07-23T01:00:00+02:00</updated>
17
+ <content type='html'>&lt;p&gt;this beep is only in English.&lt;/p&gt;</content>
18
+ </entry>
19
+ <entry>
20
+ <id>http://example.org/600</id>
21
+ <link href='http://example.org/600' rel='alternate' />
22
+ <title>600th anniversary (English)</title>
23
+ <updated>2010-07-15T12:00:00+02:00</updated>
24
+ <content type='html'>
25
+ &lt;p&gt;600 years ago &lt;a href=&quot;http://en.wikipedia.org/wiki/Battle_of_Grunwald&quot;&gt;something supposedly important&lt;/a&gt;
26
+ happened around these parts of the world&lt;/p&gt;
27
+ </content>
28
+ </entry>
29
+ <entry>
30
+ <id>http://example.org/bilingual</id>
31
+ <link href='http://example.org/bilingual' rel='alternate' />
32
+ <title>bilinguality</title>
33
+ <updated>2010-07-15T06:00:00+02:00</updated>
34
+ <content type='html'>&lt;p&gt;this article is also available &lt;a href=&quot;bilingual.pl&quot;&gt;in Polish&lt;/a&gt;&lt;/p&gt;</content>
35
+ </entry>
36
+ <entry>
37
+ <id>http://example.org/dated</id>
38
+ <link href='http://example.org/dated' rel='alternate' />
39
+ <title>a…</title>
40
+ <updated>2010-07-15T03:00:00+02:00</updated>
41
+ <content type='html'>&lt;p&gt;a dated beep.&lt;/p&gt;</content>
42
+ </entry>
43
+ <entry>
44
+ <id>http://example.org/welcome</id>
45
+ <link href='http://example.org/welcome' rel='alternate' />
46
+ <title>welcome to tributary</title>
47
+ <updated>2010-07-15T00:00:00+02:00</updated>
48
+ <content type='html'>&lt;p&gt;tributary &lt;em&gt;welcome&lt;/em&gt; article&lt;/p&gt;</content>
49
+ </entry>
50
+ </feed>
@@ -0,0 +1,50 @@
1
+ <?xml version='1.0' encoding='utf-8' ?>
2
+ <feed xmlns='http://www.w3.org/2005/Atom'>
3
+ <author>
4
+ <name>Ary Tribut</name>
5
+ </author>
6
+ <generator uri='http://github.com/chastell/tributary'>tributary</generator>
7
+ <id>http://example.org/</id>
8
+ <link href='http://example.org/' rel='alternate' />
9
+ <link href='http://example.org/index.en.xml' rel='self' />
10
+ <title>a tributary site</title>
11
+ <updated>2010-07-23T02:00:00+02:00</updated>
12
+ <entry>
13
+ <id>http://example.org/polish</id>
14
+ <link href='http://example.org/polish' rel='alternate' />
15
+ <title>ten…</title>
16
+ <updated>2010-07-23T02:00:00+02:00</updated>
17
+ <content type='html'>&lt;p&gt;ten beep jest tylko po polsku.&lt;/p&gt;</content>
18
+ </entry>
19
+ <entry>
20
+ <id>http://example.org/english</id>
21
+ <link href='http://example.org/english' rel='alternate' />
22
+ <title>this…</title>
23
+ <updated>2010-07-23T01:00:00+02:00</updated>
24
+ <content type='html'>&lt;p&gt;this beep is only in English.&lt;/p&gt;</content>
25
+ </entry>
26
+ <entry>
27
+ <id>http://example.org/600</id>
28
+ <link href='http://example.org/600' rel='alternate' />
29
+ <title>600th anniversary (English)</title>
30
+ <updated>2010-07-15T12:00:00+02:00</updated>
31
+ <content type='html'>
32
+ &lt;p&gt;600 years ago &lt;a href=&quot;http://en.wikipedia.org/wiki/Battle_of_Grunwald&quot;&gt;something supposedly important&lt;/a&gt;
33
+ happened around these parts of the world&lt;/p&gt;
34
+ </content>
35
+ </entry>
36
+ <entry>
37
+ <id>http://example.org/bilingual</id>
38
+ <link href='http://example.org/bilingual' rel='alternate' />
39
+ <title>bilinguality</title>
40
+ <updated>2010-07-15T06:00:00+02:00</updated>
41
+ <content type='html'>&lt;p&gt;this article is also available &lt;a href=&quot;bilingual.pl&quot;&gt;in Polish&lt;/a&gt;&lt;/p&gt;</content>
42
+ </entry>
43
+ <entry>
44
+ <id>http://example.org/dated</id>
45
+ <link href='http://example.org/dated' rel='alternate' />
46
+ <title>a…</title>
47
+ <updated>2010-07-15T03:00:00+02:00</updated>
48
+ <content type='html'>&lt;p&gt;a dated beep.&lt;/p&gt;</content>
49
+ </entry>
50
+ </feed>
@@ -0,0 +1,50 @@
1
+ <?xml version='1.0' encoding='utf-8' ?>
2
+ <feed xmlns='http://www.w3.org/2005/Atom'>
3
+ <author>
4
+ <name>Ary Tribut</name>
5
+ </author>
6
+ <generator uri='http://github.com/chastell/tributary'>tributary</generator>
7
+ <id>http://example.org/</id>
8
+ <link href='http://example.org/' rel='alternate' />
9
+ <link href='http://example.org/index.pl.pl.xml' rel='self' />
10
+ <title>a tributary site</title>
11
+ <updated>2010-07-23T02:00:00+02:00</updated>
12
+ <entry>
13
+ <id>http://example.org/polish</id>
14
+ <link href='http://example.org/polish' rel='alternate' />
15
+ <title>ten…</title>
16
+ <updated>2010-07-23T02:00:00+02:00</updated>
17
+ <content type='html'>&lt;p&gt;ten beep jest tylko po polsku.&lt;/p&gt;</content>
18
+ </entry>
19
+ <entry>
20
+ <id>http://example.org/600</id>
21
+ <link href='http://example.org/600' rel='alternate' />
22
+ <title>600th anniversary (intl.)</title>
23
+ <updated>2010-07-15T12:00:00+02:00</updated>
24
+ <content type='html'>
25
+ &lt;p&gt;600 years ago &lt;a href=&quot;http://en.wikipedia.org/wiki/Battle_of_Grunwald&quot;&gt;something supposedly important&lt;/a&gt;
26
+ happened around these parts of the world&lt;/p&gt;
27
+ </content>
28
+ </entry>
29
+ <entry>
30
+ <id>http://example.org/bilingual</id>
31
+ <link href='http://example.org/bilingual' rel='alternate' />
32
+ <title>dwujęzyczność</title>
33
+ <updated>2010-07-15T06:00:00+02:00</updated>
34
+ <content type='html'>&lt;p&gt;ten wpis jest także dostępny &lt;a href=&quot;bilingual.en&quot;&gt;po angielsku&lt;/a&gt;&lt;/p&gt;</content>
35
+ </entry>
36
+ <entry>
37
+ <id>http://example.org/dated</id>
38
+ <link href='http://example.org/dated' rel='alternate' />
39
+ <title>a…</title>
40
+ <updated>2010-07-15T03:00:00+02:00</updated>
41
+ <content type='html'>&lt;p&gt;a dated beep.&lt;/p&gt;</content>
42
+ </entry>
43
+ <entry>
44
+ <id>http://example.org/welcome</id>
45
+ <link href='http://example.org/welcome' rel='alternate' />
46
+ <title>welcome to tributary</title>
47
+ <updated>2010-07-15T00:00:00+02:00</updated>
48
+ <content type='html'>&lt;p&gt;tributary &lt;em&gt;welcome&lt;/em&gt; article&lt;/p&gt;</content>
49
+ </entry>
50
+ </feed>
@@ -0,0 +1,50 @@
1
+ <?xml version='1.0' encoding='utf-8' ?>
2
+ <feed xmlns='http://www.w3.org/2005/Atom'>
3
+ <author>
4
+ <name>Ary Tribut</name>
5
+ </author>
6
+ <generator uri='http://github.com/chastell/tributary'>tributary</generator>
7
+ <id>http://example.org/</id>
8
+ <link href='http://example.org/' rel='alternate' />
9
+ <link href='http://example.org/index.pl.xml' rel='self' />
10
+ <title>a tributary site</title>
11
+ <updated>2010-07-23T02:00:00+02:00</updated>
12
+ <entry>
13
+ <id>http://example.org/polish</id>
14
+ <link href='http://example.org/polish' rel='alternate' />
15
+ <title>ten…</title>
16
+ <updated>2010-07-23T02:00:00+02:00</updated>
17
+ <content type='html'>&lt;p&gt;ten beep jest tylko po polsku.&lt;/p&gt;</content>
18
+ </entry>
19
+ <entry>
20
+ <id>http://example.org/english</id>
21
+ <link href='http://example.org/english' rel='alternate' />
22
+ <title>this…</title>
23
+ <updated>2010-07-23T01:00:00+02:00</updated>
24
+ <content type='html'>&lt;p&gt;this beep is only in English.&lt;/p&gt;</content>
25
+ </entry>
26
+ <entry>
27
+ <id>http://example.org/600</id>
28
+ <link href='http://example.org/600' rel='alternate' />
29
+ <title>600th anniversary (intl.)</title>
30
+ <updated>2010-07-15T12:00:00+02:00</updated>
31
+ <content type='html'>
32
+ &lt;p&gt;600 years ago &lt;a href=&quot;http://en.wikipedia.org/wiki/Battle_of_Grunwald&quot;&gt;something supposedly important&lt;/a&gt;
33
+ happened around these parts of the world&lt;/p&gt;
34
+ </content>
35
+ </entry>
36
+ <entry>
37
+ <id>http://example.org/bilingual</id>
38
+ <link href='http://example.org/bilingual' rel='alternate' />
39
+ <title>dwujęzyczność</title>
40
+ <updated>2010-07-15T06:00:00+02:00</updated>
41
+ <content type='html'>&lt;p&gt;ten wpis jest także dostępny &lt;a href=&quot;bilingual.en&quot;&gt;po angielsku&lt;/a&gt;&lt;/p&gt;</content>
42
+ </entry>
43
+ <entry>
44
+ <id>http://example.org/dated</id>
45
+ <link href='http://example.org/dated' rel='alternate' />
46
+ <title>a…</title>
47
+ <updated>2010-07-15T03:00:00+02:00</updated>
48
+ <content type='html'>&lt;p&gt;a dated beep.&lt;/p&gt;</content>
49
+ </entry>
50
+ </feed>
@@ -0,0 +1,50 @@
1
+ <?xml version='1.0' encoding='utf-8' ?>
2
+ <feed xmlns='http://www.w3.org/2005/Atom'>
3
+ <author>
4
+ <name>Ary Tribut</name>
5
+ </author>
6
+ <generator uri='http://github.com/chastell/tributary'>tributary</generator>
7
+ <id>http://example.org/</id>
8
+ <link href='http://example.org/' rel='alternate' />
9
+ <link href='http://example.org/index.xml' rel='self' />
10
+ <title>a tributary site</title>
11
+ <updated>2010-07-23T02:00:00+02:00</updated>
12
+ <entry>
13
+ <id>http://example.org/polish</id>
14
+ <link href='http://example.org/polish' rel='alternate' />
15
+ <title>ten…</title>
16
+ <updated>2010-07-23T02:00:00+02:00</updated>
17
+ <content type='html'>&lt;p&gt;ten beep jest tylko po polsku.&lt;/p&gt;</content>
18
+ </entry>
19
+ <entry>
20
+ <id>http://example.org/english</id>
21
+ <link href='http://example.org/english' rel='alternate' />
22
+ <title>this…</title>
23
+ <updated>2010-07-23T01:00:00+02:00</updated>
24
+ <content type='html'>&lt;p&gt;this beep is only in English.&lt;/p&gt;</content>
25
+ </entry>
26
+ <entry>
27
+ <id>http://example.org/600</id>
28
+ <link href='http://example.org/600' rel='alternate' />
29
+ <title>600th anniversary (intl.)</title>
30
+ <updated>2010-07-15T12:00:00+02:00</updated>
31
+ <content type='html'>
32
+ &lt;p&gt;600 years ago &lt;a href=&quot;http://en.wikipedia.org/wiki/Battle_of_Grunwald&quot;&gt;something supposedly important&lt;/a&gt;
33
+ happened around these parts of the world&lt;/p&gt;
34
+ </content>
35
+ </entry>
36
+ <entry>
37
+ <id>http://example.org/bilingual</id>
38
+ <link href='http://example.org/bilingual' rel='alternate' />
39
+ <title>bilinguality</title>
40
+ <updated>2010-07-15T06:00:00+02:00</updated>
41
+ <content type='html'>&lt;p&gt;this article is also available &lt;a href=&quot;bilingual.pl&quot;&gt;in Polish&lt;/a&gt;&lt;/p&gt;</content>
42
+ </entry>
43
+ <entry>
44
+ <id>http://example.org/dated</id>
45
+ <link href='http://example.org/dated' rel='alternate' />
46
+ <title>a…</title>
47
+ <updated>2010-07-15T03:00:00+02:00</updated>
48
+ <content type='html'>&lt;p&gt;a dated beep.&lt;/p&gt;</content>
49
+ </entry>
50
+ </feed>