tributary 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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>