Six lines of almost sinatra “unobfuscated” and bundled up for easy (re)use
github: rubylibs/almost-sinatra, rubygems: almost-sinatra, rdoc: almost-sinatra ++ more: comments on reddit, please!
Simple (yet powerful and flexible) micro webframework.
require 'sinatra'
get '/' do
'Hallo Vienna! Servus Wien!'
end
Trivia Quiz - Q: How Many Lines of Ruby Code?
Sinatra refactored, only six lines now.
Library dependencies: Tilt and Rack (like Sinatra).
A hack by Konstantin Haase.
%w.rack tilt backports date INT TERM..map{|l|trap(l){$r.stop}rescue require l};$u=Date;$z=($u.new.year + 145).abs;puts "== Almost Sinatra/No Version has taken the stage on #$z for development with backup from Webrick"
$n=Sinatra=Module.new{a,D,S,q=Rack::Builder.new,Object.method(:define_method),/@@ *([^\n]+)\n(((?!@@)[^\n]*\n)*)/m
%w[get post put delete].map{|m|D.(m){|u,&b|a.map(u){run->(e){[200,{"Content-Type"=>"text/html"},[a.instance_eval(&b)]]}}}}
Tilt.default_mapping.lazy_map.map{|k,v|D.(k){|n,*o|$t||=(h=$u._jisx0301("hash, please");File.read(caller[0][/^[^:]+/]).scan(S){|a,b|h[a]=b};h);Kernel.const_get(v[0][0]).new(*o){n=="#{n}"?n:$t[n.to_s]}.render(a,o[0].try(:[],:locals)||{})}}
%w[set enable disable configure helpers use register].map{|m|D.(m){|*_,&b|b.try :[]}};END{Rack::Handler.get("webrick").run(a,Port:$z){|s|$r=s}}
%w[params session].map{|m|D.(m){q.send m}};a.use Rack::Session::Cookie;a.use Rack::Lock;D.(:before){|&b|a.use Rack::Config,&b};before{|e|q=Rack::Request.new e;q.params.dup.map{|k,v|params[k.to_sym]=v}}}
(Source: almost_sinatra.rb)
%w.rack tilt date INT TERM..map{|l|trap(l){$r.stop}rescue require l};$u=Date;$z=($u.new.year + 145).abs;puts "== Almost Sinatra/No Version has taken the stage on #$z for development with backup from Webrick"
Breakdown:
require 'rack'
require 'tilt'
trap( 'INT' ) { $server.stop } # rename $r to $server
trap( 'TERM' ) { $server.stop }
$port = 4567 # rename $z to $port
puts "== Almost Sinatra has taken the stage on #{$port} for development with backup from Webrick"
Aside - What’s rack?
Lets you mix ‘n’ match servers and apps.
Lets you stack apps inside apps inside apps inside apps inside apps.
Good News: A Sinatra app is a Rack app.
Learn more about Rack @ rack.github.io
.
Aside - What’s tilt?
Tilt offers a standard “generic” interface for template engines.
Let’s check-up what formats and template engines tilt includes out-of-the-box:
require 'tilt'
Tilt.mappings.each do |ext, engines|
puts "#{ext.ljust(12)} : #{engines.inspect}"
end
Will result in:
str : [Tilt::StringTemplate]
erb : [Tilt::ErubisTemplate, Tilt::ERBTemplate]
rhtml : [Tilt::ErubisTemplate, Tilt::ERBTemplate]
erubis : [Tilt::ErubisTemplate]
etn : [Tilt::EtanniTemplate]
etanni : [Tilt::EtanniTemplate]
haml : [Tilt::HamlTemplate]
sass : [Tilt::SassTemplate]
scss : [Tilt::ScssTemplate]
less : [Tilt::LessTemplate]
rcsv : [Tilt::CSVTemplate]
coffee : [Tilt::CoffeeScriptTemplate]
nokogiri : [Tilt::NokogiriTemplate]
builder : [Tilt::BuilderTemplate]
mab : [Tilt::MarkabyTemplate]
liquid : [Tilt::LiquidTemplate]
radius : [Tilt::RadiusTemplate]
markdown : [Tilt::RedcarpetTemplate, Tilt::RedcarpetTemplate::Redcarpet2, Tilt::RedcarpetTemplate::Redcarpet1, Tilt::RDiscountTemplate, Tilt::BlueClothTemplate, Tilt::KramdownTemplate, Tilt::MarukuTemplate]
mkd : [Tilt::RedcarpetTemplate, Tilt::RedcarpetTemplate::Redcarpet2, Tilt::RedcarpetTemplate::Redcarpet1, Tilt::RDiscountTemplate, Tilt::BlueClothTemplate, Tilt::KramdownTemplate, Tilt::MarukuTemplate]
md : [Tilt::RedcarpetTemplate, Tilt::RedcarpetTemplate::Redcarpet2, Tilt::RedcarpetTemplate::Redcarpet1, Tilt::RDiscountTemplate, Tilt::BlueClothTemplate, Tilt::KramdownTemplate, Tilt::MarukuTemplate]
textile : [Tilt::RedClothTemplate]
rdoc : [Tilt::RDocTemplate]
wiki : [Tilt::WikiClothTemplate, Tilt::CreoleTemplate]
creole : [Tilt::CreoleTemplate]
mediawiki : [Tilt::WikiClothTemplate]
mw : [Tilt::WikiClothTemplate]
yajl : [Tilt::YajlTemplate]
ad : [Tilt::AsciidoctorTemplate]
adoc : [Tilt::AsciidoctorTemplate]
asciidoc : [Tilt::AsciidoctorTemplate]
html : [Tilt::PlainTemplate]
$n=Module.new{extend Rack;a,D,S,q=Rack::Builder.new,Object.method(:define_method),/@@ *([^\n]+)\n(((?!@@)[^\n]*\n)*)/m
Breakdown:
$n = Module.new do
app = Rack::Builder.new # rename a to app
req = nil # rename q to req
%w[get post put delete].map{|m|D.(m){|u,&b|a.map(u){run->(e){[200,{"Content-Type"=>"text/html"},[a.instance_eval(&b)]]}}}}
Breakdown:
['get','post','put','delete'].each do |method|
define_method method do |path, &block|
app.map( path ) do
run ->(env){ [200, {'Content-Type'=>'text/html'}, [app.instance_eval( &block )]]}
end
end
end
Tilt.mappings.map{|k,v|D.(k){|n,*o|$t||=(h=$u._jisx0301("hash, please");File.read(caller[0][/^[^:]+/]).scan(S){|a,b|h[a]=b};h);v[0].new(*o){n=="#{n}"?n:$t[n.to_s]}.render(a,o[0].try(:[],:locals)||{})}}
Breakdown:
Tilt.mappings.each do |ext, engines| # rename k to ext and v to engines
define_method ext do |text, *args| # rename n to text and o to args
template = engines[0].new(*args) do
text
end
locals = (args[0].respond_to?(:[]) ? args[0][:locals] : nil) || {} # was o[0].try(:[],:locals)||{}
template.render( app, locals )
end
end
Commentary: Almost Sinatra will define a method for every format so you can use, for example:
markdown "Strong emphasis, aka bold, with **asterisks** or __underscores__."
or
erb "Hello <%= name %>!", locals: { name: params['name'] }
%w[set enable disable configure helpers use register].map{|m|D.(m){|*_,&b|b.try :[]}};END{Rack::Handler.get("webrick").run(a,Port:$z){|s|$r=s}}
Breakdown:
# was END { ... }; change to run! method
define_method 'run!' do
Rack::Handler.get('webrick').run( app, Port:$port ) {|server| $server=server }
end
%w[params session].map{|m|D.(m){q.send m}};a.use Rack::Session::Cookie;a.use Rack::Lock;D.(:before){|&b|a.use Rack::Config,&b};before{|e|q=Rack::Request.new e;q.params.dup.map{|k,v|params[k.to_sym]=v}}}
Breakdown:
['params','session'].each do |method|
define_method method do
req.send method
end
end
app.use Rack::Session::Cookie
app.use Rack::Lock
app.use Rack::Config do |env|
req = Rack::Request.new( env )
end
end # Module.new
samples/hello.rb
:
require 'almost-sinatra'
include $n # include "anonymous" Almost Sinatra DSL module
get '/hello' do
erb "Hello <%= name %>!", locals: { name: params['name'] }
end
get '/' do
markdown <<EOS
## Welcome to Almost Sinatra
A six line ruby hack by Konstantin Haase.
Try:
- [Say hello!](/hello?name=Nancy)
Powered by Almost Sinatra (#{Time.now})
EOS
end
run!
Use
$ ruby -I ./lib ./samples/hello.rb
Micro “framework” for building HTTP JSON APIs (about 100 lines of code). Example:
get '/beer/:key' do
Beer.find_by_key! params[ :key ]
end
get '/brewery/:key' do
Brewery.find_by_key! params[ :key ]
end
“Calculate” the Sinatra Port 4567:
(Date.new.year + 145).abs # Date.new.year always returns -4712, the default value for years
# => 4567
“Get” empty Hash (e.g. {}
):
Date._jisx0301("hash, please")
# => {}
Get a free wiener lager, welsh red ale or kriek lambic beer delivered to your home (computer) in JSON and much much more
github: rubylibs/webservice, rubygems: webservice, rdoc: webservice ++ more: comments on reddit, please!
The webservice library lets you script HTTP JSON APIs also known as
web services or microservices in classy Sinatra 2.0-style get
/ post
methods
with Mustermann 1.0 route / url pattern matching.
You can load web services at-runtime from files using Webservice.load_file
.
Example:
# service.rb
get '/' do
'Hello, world!'
end
and
# server.rb
require 'webservice'
App = Webservice.load_file( './service.rb' )
App.run!
and to run type
$ ruby ./server.rb
# server.rb
require 'webservice'
class App < Webservice::Base
get '/' do
'Hello, world!'
end
end
App.run!
and to run type
$ ruby ./server.rb
Use config.ru
and rackup
. Example:
# config.ru
require `webservice`
class App < Webservice::Base
get '/' do
'Hello, world!'
end
end
run App
and to run type
$ rackup # will (auto-)load config.ru
Note: config.ru
is a shortcut (inline)
version of Rack::Builder.new do ... end
:
# server.rb
require 'webservice'
class App < Webservice::Base
get '/' do
'Hello, world!'
end
end
builder = Rack::Builder.new do
run App
end
Rack::Server.start builder.to_app
and to run type
$ ruby ./server.rb
See
beerkit / beer.db.service
-
beer.db HTTP JSON API (web service) scripts e.g.
get '/beer/(r|rnd|rand|random)' do # special keys for random beer
Beer.rnd
end
get '/beer/:key'
Beer.find_by! key: params['key']
end
get '/brewery/(r|rnd|rand|random)' do # special keys for random brewery
Brewery.rnd
end
get '/brewery/:key'
Brewery.find_by! key: params['key']
end
...
worlddb / world.db.service
-
world.db HTTP JSON API (web service) scripts
get '/countries(.:format)?' do
Country.by_key.all # sort/order by key
end
get '/cities(.:format)?' do
City.by_key.all # sort/order by key
end
get '/tag/:slug(.:format)?' do # e.g. /tag/north_america.csv
Tag.find_by!( slug: params['slug'] ).countries
end
...
sportdb / sport.db.service
-
sport.db (football.db) HTTP JSON API (web service) scripts
# to be done
...
Class Model Diagrams
Everything is a place.
Country
Model - Example:
at = Country.find_by! key: 'at'
at.name
# => 'Austria'
at.pop
# => 8_414_638
at.area
# => 83_871
at.states.count
# => 9
at.states
# => [ 'Wien', 'Niederösterreich', 'Oberösterreich', ... ]
at.cities.by_pop
# => [ 'Wien', 'Graz', 'Linz', 'Salzburg', 'Innsbruck' ... ]
City
Model - Example:
c = City.find_by! key: 'wien'
c.name
# => 'Wien'
c.country.name
# => 'Austria'
c.country.continent.name
# => 'Europe'
la = City.find_by! key: 'losangeles'
la.name
# => 'Los Angeles'
la.state.name
# => 'California'
la.state.key
# => 'ca'
la.country.name
# => 'United States'
la.country.key
# => 'us'
la.country.continent.name
# => 'North America'
Tag
Model - Example:
euro = Tag.find_by! key: 'euro'
euro.countries.count
# => 17
euro.countries
# => ['Austria, 'Belgium', 'Cyprus', ... ]
flanders = Tag.find_by! key: 'flanders'
flanders.states.count
# => 5
flanders.states
# => ['Antwerpen', 'Brabant Wallon', 'Limburg', 'Oost-Vlaanderen', 'West-Vlaanderen']
flanders.states.first.country.name
# => 'Belgium'
and so on.
The worlddb web service starter sample lets you build your own HTTP JSON API
using the
world.db
. Example:
class StarterApp < Webservice::Base
#####################
# Models
include WorldDb::Models # e.g. Continent, Country, State, City, etc.
##############################################
# Controllers / Routing / Request Handlers
get '/countries(.:format)?' do
Country.by_key.all # sort/order by key
end
get '/cities(.:format)?' do
City.by_key.all # sort/order by key
end
get '/tag/:slug(.:format)?' do # e.g. /tag/north_america.csv
Tag.find_by!( slug: params['slug'] ).countries
end
...
end # class StarterApp
(Source: app.rb
)
Step 1: Install all libraries (Ruby gems) using bundler. Type:
$ bundle install
Step 2: Copy an SQLite database e.g. world.db
into your folder.
Step 3: Startup the web service (HTTP JSON API). Type:
$ ruby ./server.rb
That’s it. Open your web browser and try some services
running on your machine on port 9292 (e.g. localhost:9292
). Example:
List all the world countries (in JSON - the default format):
http://localhost:9292/countries
List all the world countries (in CSV):
http://localhost:9292/countries.csv
List all the world countries (in HTML w/ simple table):
http://localhost:9292/countries.html
List all cities (in JSON):
http://localhost:9292/cities
List all cities (in CSV):
http://localhost:9292/cities.csv
List all countries tagged with north_america
(in JSON):
http://localhost:9292/tag/north_america
List all countries tagged with north_america
(in CSV):
http://localhost:9292/tag/north_america.csv
And so on. Now change the app.rb
script to fit your needs. Be bold. Enjoy.
world.db HTTP JSON API (web service) scripts
You can run any of the scripts using the worlddb
command line tool. By default the serve
command will look for
a script named Service
or service.rb
(in the working folder, that is, ./
). Example:
$ worlddb serve
To run any other script - copy the script into the working folder and pass it along as an argument. Example:
$ worlddb serve starter # note: will (auto-)add the .rb extension or
$ worlddb serve starter.rb
get '/countries(.:format)?' do
Country.by_key.all # sort/order by key
end
get '/cities(.:format)?' do
City.by_key.all # sort/order by key
end
get '/tag/:slug(.:format)?' do # e.g. /tag/north_america.csv
Tag.find_by!( slug: params['slug'] ).countries
end
(Source: world.db.service/starter.rb)
get '/countries(.:format)?' do
Country.by_key.all # sort/order by key
end
get '/cities(.:format)?' do
City.by_key.all # sort/order by key
end
get '/continents(.:format)?' do
Continent.all
end
get '/tag/:slug(.:format)?' do # e.g. /tag/north_america.csv
Tag.find_by!( slug: params['slug'] ).countries
end
(Source: world.db.service/service.rb)
Cheers, Prost, Kampai, Na zdravi, Salute, 乾杯, Skål, Egészségedre!
github: beerkit/beer.db, rubygems: beerdb, rdoc: beerdb ++ more: comments on reddit, please!
The beerdb library offers a ready-to-use database schema (in SQL)
and models such as - surprise, surprise -
Beer
, Brewery
, Brand
and friends (using the ActiveRecord
object-relational mapper machinery). Example:
Let’s try the brewery model:
by = Brewery.find_by( key: 'guinness' )
by.title
#=> 'St. James's Gate Brewery / Guinness Brewery'
by.country.key
#=> 'ie'
by.country.title
#=> 'Ireland'
by.city.title
#=> 'Dublin'
by.beers.first
#=> 'Guinness', 4.2
...
Or let’s try the beer model:
b = Beer.find_by( key: 'guinness' )
b.title
#=> 'Guinness'
b.abv # that is, alcohol by volume (abv)
#=> 4.2
b.tags
#=> 'irish_dry_stout', 'dry_stout', 'stout'
b.brewery.title
#=> 'St. James's Gate Brewery / Guinness Brewery'
...
What’s it good for? Good question. Let’s build an HTTP JSON service
that serves up a Guinness Irish Stout
or a Bamberg Aecht Schlenkerla Rauchbier Märzen as JSON?
Example - GET /beer/guinness
:
{
"key": "guinness",
"title": "Guinness",
"synonyms": "Guinness Draught",
"abv": "4.2",
"srm": null,
"og": null,
"tags": ["irish_dry_stout","dry_stout","stout"],
"brewery":
{
"key": "guinness",
"title": "St. James's Gate Brewery / Guinness Brewery"
},
"country":
{
"key": "ie",
"title": "Irland"
}
}
Let’s use the Sinatra-like webservice library that offers a mini language,
that is, domain-specific language (DSL)
that lets you define routes, that is, HTTP methods paired with an URL-matching pattern
and much more.
For example, you can code the GET /beer/guinness
route in
the webservice library as get '/beer/guinness'
.
To make it into a route for any beer lets replace the guinness
beer key
with a placeholder, thus, resulting in get '/beer/:key'
. Let’s run it:
service.rb
:
class BeerService < Webservice::Base
include BeerDb::Models # lets (re)use the Beer, Brewery, etc. models
get '/beer/:key' do
Beer.find_by!( key: params[:key] )
end
end
That’s it. Ready to serve. Let’s boot-up the beer service with a web server (e.g. Thin) using a Rack handler. Example:
boot.rb
:
require 'webservice' # note: webservice will pull in web server machinery (e.g. rack, thin, etc.)
require 'beerdb/models' # note: beerdb will pull in database access machinery (e.g. activerecord, etc.)
# database setup 'n' config
ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: './beer.db' )
require './service'
Rack::Handler::Thin.run BeerService, :Port => 9292
Try:
$ ruby ./boot.rb
Open up your browser and try http://localhost:9292/beer/guinness
.
Voila. Enjoy your Guinness irish stout responsibly.
Let’s add brewery details to the beer service and lets add a new GET /brewery
route. Example:
get '/beer/:key' do
beer = Beer.find_by!( key: params[ :key ] )
brewery = {}
if beer.brewery.present?
brewery = { key: beer.brewery.key,
title: beer.brewery.title }
end
tags = []
if beer.tags.present?
beer.tags.each { |tag| tags << tag.key }
end
{ key: beer.key,
title: beer.title,
synonyms: beer.synonyms,
abv: beer.abv,
srm: beer.srm,
og: beer.og,
tags: tags,
brewery: brewery,
country: { key: beer.country.key,
title: beer.country.title }
}
end
get '/brewery/:key' do
brewery = Brewery.find_by!( key: params[:key] )
beers = []
brewery.beers.each do |b|
beers << { key: b.key, title: b.title }
end
tags = []
if brewery.tags.present?
brewery.tags.each { |tag| tags << tag.key }
end
{ key: brewery.key,
title: brewery.title,
synonyms: brewery.synonyms,
since: brewery.since,
address: brewery.address,
web: brewery.web,
tags: tags,
beers: beers,
country: { key: brewery.country.key,
title: brewery.country.title }
}
end
beer.db HTTP JSON API (web service) scripts
You can run any of the scripts using the beerdb
command line tool. By default the serve
command will look for
a script named Service
or service.rb
(in the working folder, that is, ./
). Example:
$ beerdb serve
To run any other script - copy the script into the working folder and pass it along as an argument. Example:
$ beerdb serve starter # note: will (auto-)add the .rb extension or
$ beerdb serve starter.rb
get '/beer/(r|rnd|rand|random)' do ## special keys for random beer
Beer.rnd
end
get '/beer/:key' do
Beer.find_by! key: params['key']
end
get '/brewery/(r|rnd|rand|random)' do ## special keys for random brewery
Brewery.rnd
end
get '/brewery/:key' do
Brewery.find_by! key: params['key']
end
(Source: beer.db.service/starter.rb)
get '/' do
## self-docu in json
data = {
endpoints: {
get_beer: {
doc: 'get beer by key',
url: '/beer/:key'
},
}
}
end
get '/notes/:key' do |key|
puts " handle GET /notes/:key"
if ['l', 'latest'].include?( key )
# get latest tasting notes (w/ ratings)
notes = Note.order( 'updated_at DESC' ).limit(10).all
elsif ['h', 'hot'].include?( key )
# get latest tasting notes (w/ ratings)
# fix: use log algo for "hotness" - for now same as latest
notes = Note.order( 'updated_at DESC' ).limit(10).all
elsif ['t', 'top'].include?( key )
notes = Note.order( 'rating DESC, updated_at DESC' ).limit(10).all
else
### todo: move to /u/:key/notes ??
# assume it's a user key
user = User.find_by_key!( key )
notes = Note.order( 'rating DESC, updated_at DESC' ).where( user_id: user.id ).all
end
data = []
notes.each do |note|
data << {
beer: { title: note.beer.title,
key: note.beer.key },
user: { name: note.user.name,
key: note.user.key },
rating: note.rating,
comments: note.comments,
place: note.place,
created_at: note.created_at,
updated_at: note.updated_at
}
end
data
end
get '/notes' do
if params[:method] == 'post'
puts " handle GET /notes?method=post"
user = User.find_by_key!( params[:user] )
beer = Beer.find_by_key!( params[:beer] )
rating = params[:rating].to_i
place = params[:place] # assumes for now a string or nil / pass through as is
attribs = {
user_id: user.id,
beer_id: beer.id,
rating: rating,
place: place
}
note = Note.new
note.update_attributes!( attribs )
end
{ status: 'ok' }
end
get '/drinks/:key' do |key|
puts " handle GET /drinks/:key"
if ['l', 'latest'].include?( key )
# get latest +1 drinks
## todo: order by drunk_at??
drinks = Drink.order( 'updated_at DESC' ).limit(10).all
else
### todo: move to /u/:key/drinks ??
# assume it's a user key
user = User.find_by_key!( key )
drinks = Drink.order( 'updated_at DESC' ).where( user_id: user.id ).all
end
data = []
drinks.each do |drink|
data << {
beer: { title: drink.beer.title,
key: drink.beer.key },
user: { name: drink.user.name,
key: drink.user.key },
place: drink.place,
drunk_at: drink.drunk_at,
created_at: drink.created_at,
updated_at: drink.updated_at
}
end
data
end
get '/drinks' do
if params[:method] == 'post'
puts " handle GET /drinks?method=post"
user = User.find_by_key!( params[:user] )
beer = Beer.find_by_key!( params[:beer] )
place = params[:place] # assumes for now a string or nil / pass through as is
attribs = {
user_id: user.id,
beer_id: beer.id,
place: place
}
drink = Drink.new
drink.update_attributes!( attribs )
end
{ status: 'ok' }
end
get '/beer/:key' do |key|
if ['r', 'rnd', 'rand', 'random'].include?( key )
# special key for random beer
# Note: use .first (otherwise will get ActiveRelation not Model)
beer = Beer.rnd.first
else
beer = Beer.find_by_key!( key )
end
end
get '/brewery/:key' do |key|
if ['r', 'rnd', 'rand', 'random'].include?( key )
# special key for random brewery
# Note: use .first (otherwise will get ActiveRelation not Model)
brewery = Brewery.rnd.first
else
brewery = Brewery.find_by_key!( key )
end
end
(Source: beer.db.service/service.rb)
World Cup - English Premier League - Spanish La Liga - Austrian Bundesliga :-) And Much More
github: sportdb/sport.db, rubygems: sportdb, rdoc: sportdb ++ more: comments on reddit, please!
A library and command line tool that ships with an ready-to-use sport.db SQL schema and ActiveRecord models and (structured) text-to-data readers (parsers).
Let’s get started with the Quick Starter Sample. Welcome to the Mauritius Premier Football League. Lets create your own (structured) text datasets from scratch and read it all into your SQL database of choice (e.g. SQLite, PostgreSQL, etc.) with a single command e.g.:
$ sportdb build
Let’s get started. Follow along these six steps:
Using a file structure like:
├── 2014-15 # 2014-15 season folder
| ├── league-i.txt # match fixtures / results - matchdays 1-18
| ├── league-ii.txt # - matchdays 19-36
| └── league.yml # "front matter" settings
├── setups
| └── all.txt # file list (manifest)
├── leagues.txt # all leagues
├── clubs.txt # all clubs
└── Datafile # build script
Example - leagues.txt
:
mu, Mauritius Premier League
The Mauritius Premier League includes ten clubs.
Example - clubs.txt
:
joachim, Cercle de Joachim|Cercle de Joachim SC|Joachim, CDJ
chamarel, Chamarel|Chamarel SC, CHA
curepipesc, Curepipe Starlight|Curepipe SC|Starlight, CUR
entente, Entente Boulet Rouge|Entente Boulet Rouge-Riche Mare Rovers, EBR
lacure, La Cure Sylvester|La Cure, LCS
pamplemousses, Pamplemousses|Pamplemousses SC, PPM
petiteriv, Petite Rivière Noire|Petite Rivière, PRN
aspl, AS Port-Louis 2000|ASPL 2000, APL
qbornes, AS Quatre Bornes|Quatre Bornes, AQB
rempart, AS Rivière du Rempart|Rivière du Rempart, ARR
Note: Use the pipe (|
) to list alternative names.
Example - 2014-15/league-i.txt
:
Matchday 1
[Wed Nov/5]
Curepipe Starlight 1-3 Petite Rivière Noire
AS Quatre Bornes 1-0 La Cure Sylvester
Pamplemousses 0-1 Rivière du Rempart
AS Port-Louis 2000 5-1 Entente Boulet Rouge
Chamarel FC 2-3 Cercle de Joachim
Matchday 2
[Sun Nov/9]
Curepipe Starlight 2-1 AS Quatre Bornes
Entente Boulet Rouge 1-2 Chamarel FC
Rivière du Rempart 1-1 AS Port-Louis 2000
La Cure Sylvester 1-2 Pamplemousses
Petite Rivière Noire 2-0 Cercle de Joachim
Matchday 3
[Wed Nov/12]
Chamarel FC 1-1 Rivière du Rempart
AS Port-Louis 2000 1-0 La Cure Sylvester
Cercle de Joachim 2-2 Entente Boulet Rouge
Pamplemousses 0-4 Curepipe Starlight
AS Quatre Bornes 1-2 Petite Rivière Noire
Matchday 4
[Sun Nov/16]
Petite Rivière Noire 4-1 Entente Boulet Rouge
Rivière du Rempart 1-1 Cercle de Joachim
La Cure Sylvester 0-0 Chamarel FC
Curepipe Starlight 0-0 AS Port-Louis 2000
AS Quatre Bornes 1-0 Pamplemousses
...
Example - 2014-15/league.yml
:
league: mu
season: 2014/15
start_at: 2014-11-05
fixtures:
- league-i
- league-ii
10 teams:
- Cercle de Joachim
- AS Port-Louis 2000
- Pamplemousses
- Curepipe Starlight
- Petite Rivière Noire
- Rivière du Rempart
- AS Quatre Bornes
- Chamarel SC
- La Cure Sylvester
- Entente Boulet Rouge
Example - setups/all.txt
:
mu-mauritius!/leagues
mu-mauritius!/clubs
mu-mauritius!/2014-15/league
Example - Datafile
:
## a) Add country e.g. Mauritius
inline do
Country.parse 'mu', 'Mauritius', 'MUS', '2_040 km²', '1_261_200'
end
## b) Read in all football datasets in ./mu-mauritius (defaults to setups/all.txt)
football 'mu-mauritius'
Now try in your working folder:
$ sportdb build
This will read in the ./Datafile
and
./sport.db
That’s it. Try:
$ sqlite3 sport.db
SQLite version 3.7.16
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables
alltime_standing_entries events_grounds names
alltime_standings events_teams parts
assocs games persons
assocs_assocs goals places
badges grounds props
cities group_standing_entries rosters
continents group_standings rounds
counties groups seasons
countries groups_teams states
country_codes langs taggings
districts leagues tags
event_standing_entries logs teams
event_standings metros usages
events munis zones
sqlite> select * from countries;
1|Mauritius|mauritius|mu|1|MUS|||1261200|2040|||f|t|f|f|
sqlite> select * from teams;
1|joachim|Cercle de Joachim||CDJ|Cercle de Joachim SC|Joachim||t|||||f|
2|chamarel|Chamarel SC||CHA|Chamarel|Chamarel Sport Club||t|||||f|
3|curepipesc|Curepipe Starlight||CUR|Curepipe Starlight SC||t|||||f|
4|entente|Entente Boulet Rouge||EBR|Entente Boulet Rouge SC|Entente Boulet Rouge-Riche Mare Rovers||t|||||f|
5|lacure|La Cure Sylvester||LCS|La Cure Sylvester SC|La Cure||t|||||f|
6|pamplemousses|Pamplemousses||PPM|Pamplemousses SC||t|||||f|
7|petiteriv|Petite Rivière Noire||PRN|Petite Rivière Noire SC|Petite Rivière||t|||||f|
8|aspl|AS Port-Louis 2000||APL|ASPL 2000|Port-Louis 2000|Association Sportive Port-Louis 2000||t|||||f|
9|qbornes|AS Quatre Bornes||AQB|ASQB|Quatre Bornes||t|||||f|
10|rempart|Rivière du Rempart||ARR|AS Rivière du Rempart||t|||||f|
11|pointauxsables|Pointe-aux-Sables Mates|||||t|||||f|
12|savanne|Savanne SC|||Savanne Sporting Club||t|||||f|
And so on.
In the openfootball/world-cup you find ready-to-use (free, public domain) datasets for all world cups including the upcoming world cup 2018 in Russia. Example - 2018–russia/cup.txt:
############################
# World Cup 2018 Russia
Group A | Russia Saudi Arabia Egypt Uruguay
Group B | Portugal Spain Morocco Iran
Group C | France Australia Peru Denmark
Group D | Argentina Iceland Croatia Nigeria
Group E | Brazil Switzerland Costa Rica Serbia
Group F | Germany Mexico Sweden South Korea
Group G | Belgium Panama Tunisia England
Group H | Poland Senegal Colombia Japan
Matchday 1 | Thu Jun/14
Matchday 2 | Fri Jun/15
Matchday 3 | Sat Jun/16
...
Group A:
(1) Thu Jun/14 18:00 Russia - Saudi Arabia @ Luzhniki Stadium, Moscow (UTC+3)
(2) Fri Jun/15 17:00 Egypt - Uruguay @ Central Stadium, Yekaterinburg (UTC+5)
...
The Future of Online News - The Future of Facebook & Co - Web Feeds, Web Feeds, Web Feeds
github: feedparser/feedparser, rubygems: feedparser, rdoc: feedparser ++ more: comments on reddit, please!
A web feed (or news feed) is a (simple) document/text format that:
(1) lets you publish a list of:
and that
(2) lets others subscribe to your updates.
Example:
{
"version": "https://jsonfeed.org/version/1",
"title": "Jason Fried's Microblog",
"home_page_url": "https://micro.blog/jasonfried/",
"feed_url": "https://micro.blog/jasonfried/feed.json",
"author": {
"name": "Jason Fried",
"url": "https://micro.blog/jasonfried/",
"avatar": "https://micro.blog/jasonfried/avatar.png"
},
"items": [
{
"id": "865767227416612864",
"url": "https://micro.blog/jasonfried/status/865767227416612864",
"content_text": "JSON Feed? I know that guy.",
"date_published": "2017-05-19T20:12:00-00:00"
}
]
}
Triva Quiz - Q: What’s RSS?
RDF = Resource Description Framework
Trivia Quiz - Find the Content - Q: What’s your favorite way to add content in hypertext to RSS 2.0?
<description>
<content:encoded>
from RDF/RSS 1.0 content module extension<media:content>
from Yahoo! search extensionBonus: Is your content in plain text, in html, in xhtml, in html escaped? Is your content a summary? or full text?
Let’s celebrate diversity! Live and let live! Web feed formats today in 2017 include:
And some more.
One library to rule them all! All your base are blong to feedparser.
In the end all formats are just 0 and 1s or:
feed.title
feed.url
feed.items[0].title
feed.items[0].url
feed.items[0].published
feed.items[0].content_html
or feed.items[0].content
feed.items[0].content_text
feed.items[0].summary
=> Let your computer handle the reading of web feeds ;-).
require 'open-uri'
require 'feedparser'
txt = open( 'http://openfootball.github.io/feed.xml' ).read
feed = FeedParser::Parser.parse( txt )
puts feed.title
# => "football.db - Open Football Data"
puts feed.url
# => "http://openfootball.github.io/"
puts feed.items[0].title
# => "football.db - League Quick Starter Sample - Mauritius Premier League - Create Your Own Repo/League(s) from Scratch"
puts feed.items[0].url
# => "http://openfootball.github.io/2015/08/30/league-quick-starter.html"
puts feed.items[0].updated
# => Sun, 30 Aug 2015 00:00:00 +0000
puts feed.items[0].content
# => "Added a new quick starter sample using the Mauritius Premier League to get you started..."
...
or reading a feed in the new JSON Feed format in - surprise, surprise - JSON; note: nothing changes :-)
txt = open( 'http://openfootball.github.io/feed.json' ).read
feed = FeedParser::Parser.parse( txt )
puts feed.title
# => "football.db - Open Football Data"
puts feed.url
# => "http://openfootball.github.io/"
puts feed.items[0].title
# => "football.db - League Quick Starter Sample - Mauritius Premier League - Create Your Own Repo/League(s) from Scratch"
puts feed.items[0].url
# => "http://openfootball.github.io/2015/08/30/league-quick-starter.html"
puts feed.items[0].updated
# => Sun, 30 Aug 2015 00:00:00 +0000
puts feed.items[0].content_text
# => "Added a new quick starter sample using the Mauritius Premier League to get you started..."
...
Microformats let you mark up feeds and posts in HTML with
h-entry
,
h-feed
,
and friends.
Note: Microformats support in feedparser is optional. Install and require the the microformats library to read feeds in HTML with Microformats.
require 'microformats'
text =<<HTML
<article class="h-entry">
<h1 class="p-name">Microformats are amazing</h1>
<p>Published by
<a class="p-author h-card" href="http://example.com">W. Developer</a>
on <time class="dt-published" datetime="2013-06-13 12:00:00">13<sup>th</sup> June 2013</time>
<p class="p-summary">In which I extoll the virtues of using microformats.</p>
<div class="e-content">
<p>Blah blah blah</p>
</div>
</article>
HTML
feed = FeedParser::Parser.parse( text )
puts feed.format
# => "html"
puts feed.items.size
# => 1
puts feed.items[0].authors.size
# => 1
puts feed.items[0].content_html
# => "<p>Blah blah blah</p>"
puts feed.items[0].content_text
# => "Blah blah blah"
puts feed.items[0].title
# => "Microformats are amazing"
puts feed.items[0].summary
# => "In which I extoll the virtues of using microformats."
puts feed.items[0].published
# => 2013-06-13 12:00:00
puts feed.items[0].authors[0].name
# => "W. Developer"
...
Planet Feed Reader in 20 Lines of Ruby
planet.rb
:
require 'open-uri'
require 'feedparser'
require 'erb'
# step 1) read a list of web feeds
FEED_URLS = [
'http://vienna-rb.at/atom.xml',
'http://weblog.rubyonrails.org/feed/atom.xml',
'http://www.ruby-lang.org/en/feeds/news.rss',
'http://openfootball.github.io/feed.json',
]
items = []
FEED_URLS.each do |url|
feed = FeedParser::Parser.parse( open( url ).read )
items += feed.items
end
# step 2) mix up all postings in a new page
FEED_ITEM_TEMPLATE = <<EOS
<% items.each do |item| %>
<div class="item">
<h2><a href="<%= item.url %>"><%= item.title %></a></h2>
<div><%= item.content %></div>
</div>
<% end %>
EOS
puts ERB.new( FEED_ITEM_TEMPLATE ).result
Run the script:
$ ruby ./planet.rb
Prints:
<div class="item">
<h2><a href="http://vienna-rb.at/blog/2017/11/06/picks/">Picks / what the vienna.rb team thinks is worth sharing this week</a></h2>
<div>
<h3>6/11 Picks!!</h3>
<p>In a series on this website we'll entertain YOU with our picks...
...
See the Planet Pluto feed reader family:
HANSON.parse, SON.parse, JSONX.parse
github: json-next/json-next, rubygems: json-next, rdoc: json-next ++ more: comments on reddit, please!
More:
''
instead of ""
)
\
or "
etc. To escape '
use '''
e.g. ''''Henry's Themes'''
set
, map
, symbol
, etc.)Fixing JSON - Comments, Please!
We can easily agree on what’s wrong with JSON, and I can’t help wondering if it’d be worth fixing it.
– Tim Bray (Fixing JSON)
XML already does everything JSON does! And there’s no way to differentiate between nodes and attributes! And there are no namespaces! And no schemas! What’s the point of JSON?
– Anonymous
We need to fix engineers that try to ‘fix JSON’, absolutely nothing is broken with JSON.
– Anonymous
The json-next library lets you convert and read (parse) next generation json versions
including: HanSON e.g. HANSON.parse
, SON e.g. SON.parse
, JSONX e.g. JSONX.parse
.
HanSON - JSON for Humans by Tim Jansen et al
HanSON is an extension of JSON with a few simple additions to the spec:
''
) are supported in addition to double quotes (""
)//
) and multi-line comments (/* */
), in all places where JSON allows whitespace.Example:
{
listName: "Sesame Street Monsters", // note that listName needs no quotes
content: [
{
name: "Cookie Monster",
/* Note the template quotes and unescaped regular quotes in the next string */
background: `Cookie Monster used to be a
monster that ate everything, especially cookies.
These days he is forced to eat "healthy" food.`
}, {
// You can single-quote strings too:
name: 'Herry Monster',
background: `Herry Monster is a furry blue monster with a purple nose.
He's mostly retired today.`
}, // don't worry, the trailing comma will be ignored
]
}
Use HANSON.convert
to convert HanSON text to ye old’ JSON text:
{
"listName": "Sesame Street Monsters",
"content": [
{ "name": "Cookie Monster",
"background": "Cookie Monster used to be a\n ... to eat \"healthy\" food."
},
{ "name": "Herry Monster",
"background": "Herry Monster is a furry blue monster with a purple nose.\n ... today."
}
]
}
Use HANSON.parse
instead of JSON.parse
to parse text to ruby hash / array / etc.:
{
"listName" => "Sesame Street Monsters",
"content" => [
{ "name" => "Cookie Monster",
"background" => "Cookie Monster used to be a\n ... to eat \"healthy\" food."
},
{ "name" => "Herry Monster",
"background" => "Herry Monster is a furry blue monster with a purple nose.\n ... today."
}
]
}
SON - Simple Object Notation by Aleksander Gurin et al
Simple data format similar to JSON, but with some minor changes:
#
sign and ends with newline (\n
)JSON is compatible with SON in a sense that JSON data is also SON data, but not vise versa.
Example:
{
# Personal information
"name": "Alexander Grothendieck"
"fields": "mathematics"
"main_topics": [
"Etale cohomology"
"Motives"
"Topos theory"
"Schemes"
]
"numbers": [1 2 3 4]
"mixed": [1.1 -2 true false null]
}
Use SON.convert
to convert SON text to ye old’ JSON text:
{
"name": "Alexander Grothendieck",
"fields": "mathematics",
"main_topics": [
"Etale cohomology",
"Motives",
"Topos theory",
"Schemes"
],
"numbers": [1, 2, 3, 4],
"mixed": [1.1, -2, true, false, null]
}
Use SON.parse
instead of JSON.parse
to parse text to ruby hash / array / etc.:
{
"name" => "Alexander Grothendieck",
"fields" => "mathematics",
"main_topics" =>
["Etale cohomology", "Motives", "Topos theory", "Schemes"],
"numbers" => [1, 2, 3, 4],
"mixed" => [1.1, -2, true, false, nil]
}
JSON with Extensions or JSON v1.1 (a.k.a. JSON11 or JSON XI or JSON II)
Includes all JSON extensions from HanSON:
''
) are supported in addition to double quotes (""
)//
) and multi-line comments (/* */
), in all places where JSON allows whitespace.Plus all JSON extensions from SON:
#
sign and ends with newline (\n
)Plus some more extra JSON extensions:
-
) too e.g. allows common keys such as core-js
, babel-preset-es2015
, eslint-config-jquery
and othersExample:
{
# use shell-like (or ruby-like) comments
listName: "Sesame Street Monsters" # note: comments after key-value pairs are optional
content: [
{
name: "Cookie Monster"
// note: the template quotes and unescaped regular quotes in the next string
background: `Cookie Monster used to be a
monster that ate everything, especially cookies.
These days he is forced to eat "healthy" food.`
}, {
// You can single-quote strings too:
name: 'Herry Monster',
background: `Herry Monster is a furry blue monster with a purple nose.
He's mostly retired today.`
}, /* don't worry, the trailing comma will be ignored */
]
}
Use JSONX.convert
(or JSONXI.convert
or JSON11.convert
or JSONII.convert
) to convert JSONX text to ye old’ JSON text:
{
"listName": "Sesame Street Monsters",
"content": [
{ "name": "Cookie Monster",
"background": "Cookie Monster used to be a\n ... to eat \"healthy\" food."
},
{ "name": "Herry Monster",
"background": "Herry Monster is a furry blue monster with a purple nose.\n ... today."
}
]
}
Use JSONX.parse
(or JSONXI.parse
or JSON11.parse
or JSONII.parse
) instead of JSON.parse
to parse text to ruby hash / array / etc.:
{
"listName" => "Sesame Street Monsters",
"content" => [
{ "name" => "Cookie Monster",
"background" => "Cookie Monster used to be a\n ... to eat \"healthy\" food."
},
{ "name" => "Herry Monster",
"background" => "Herry Monster is a furry blue monster with a purple nose.\n ... today."
}
]
}
require 'json/next'
text1 =<<TXT
{
listName: "Sesame Street Monsters", // note that listName needs no quotes
content: [
{
name: "Cookie Monster",
/* Note the template quotes and unescaped regular quotes in the next string */
background: `Cookie Monster used to be a
monster that ate everything, especially cookies.
These days he is forced to eat "healthy" food.`
}, {
// You can single-quote strings too:
name: 'Herry Monster',
background: `Herry Monster is a furry blue monster with a purple nose.
He's mostly retired today.`
}, // don't worry, the trailing comma will be ignored
]
}
TXT
pp HANSON.parse( text1 ) # note: is the same as JSON.parse( HANSON.convert( text ))
resulting in:
{
"listName" => "Sesame Street Monsters",
"content" => [
{ "name" => "Cookie Monster",
"background" => "Cookie Monster used to be a\n ... to eat \"healthy\" food."
},
{ "name" => "Herry Monster",
"background" => "Herry Monster is a furry blue monster with a purple nose.\n ... today."
}
]
}
and
text2 =<<TXT
{
# Personal information
"name": "Alexander Grothendieck"
"fields": "mathematics"
"main_topics": [
"Etale cohomology"
"Motives"
"Topos theory"
"Schemes"
]
"numbers": [1 2 3 4]
"mixed": [1.1 -2 true false null]
}
TXT
pp SON.parse( text2 ) # note: is the same as JSON.parse( SON.convert( text ))
resulting in:
{
"name" => "Alexander Grothendieck",
"fields" => "mathematics",
"main_topics" =>
["Etale cohomology", "Motives", "Topos theory", "Schemes"],
"numbers" => [1, 2, 3, 4],
"mixed" => [1.1, -2, true, false, nil]
}
and
text3 =<<TXT
{
# use shell-like (or ruby-like) comments
listName: "Sesame Street Monsters" # note: comments after key-value pairs are optional
content: [
{
name: "Cookie Monster"
// note: the template quotes and unescaped regular quotes in the next string
background: `Cookie Monster used to be a
monster that ate everything, especially cookies.
These days he is forced to eat "healthy" food.`
}, {
// You can single-quote strings too:
name: 'Herry Monster',
background: `Herry Monster is a furry blue monster with a purple nose.
He's mostly retired today.`
}, /* don't worry, the trailing comma will be ignored */
]
}
TXT
pp JSONX.parse( text3 ) # note: is the same as JSON.parse( JSONX.convert( text ))
pp JSONXI.parse( text3 ) # note: is the same as JSON.parse( JSONXI.convert( text ))
pp JSON11.parse( text3 ) # note: is the same as JSON.parse( JSON11.convert( text ))
pp JSONII.parse( text3 ) # note: is the same as JSON.parse( JSONII.convert( text ))
resulting in:
{
"listName" => "Sesame Street Monsters",
"content" => [
{ "name" => "Cookie Monster",
"background" => "Cookie Monster used to be a\n ... to eat \"healthy\" food."
},
{ "name" => "Herry Monster",
"background" => "Herry Monster is a furry blue monster with a purple nose.\n ... today."
}
]
}
See the Awesome JSON (What’s Next?) collection / page.
github: feedtxt/feedtxt, rubygems: feedtxt, rdoc: feedtxt
Use Feedtxt.parse
to read / parse feeds in text using the Feed.TXT
format also known as RSS (Really Simple Sharing) 5.0 ;-).
The parse method will return an array:
[ feed_metadata,
[
[ item_metadata, item_content ],
[ item_metadata, item_content ],
...
]
]
Easier to see it in action. Let’s read in:
require 'feedtxt'
text =<<TXT
|>>>
title: "My Example Feed"
home_page_url: "https://example.org/"
feed_url: "https://example.org/feed.txt"
</>
id: "2"
url: "https://example.org/second-item"
---
This is a second item.
</>
id: "1"
url: "https://example.org/initial-post"
---
Hello, world!
<<<|
TXT
feed = Feedtxt.parse( text )
pp feed
resulting in:
[
{"title" =>"My Example Feed",
"home_page_url"=>"https://example.org/",
"feed_url" =>"https://example.org/feed.txt"
},
[[
{"id" =>"2",
"url"=>"https://example.org/second-item"
},
"This is a second item."
],
[
{"id"=>"1",
"url"=>"https://example.org/initial-post"
},
"Hello, world!"
]]
]
and use like:
feed_metadata = feed[0]
feed_items = feed[1]
feed_metadata[ 'title' ]
# => "My Example Feed"
feed_metadata[ 'feed_url' ]
# => "https://example.org/feed.txt"
item = feed_items[0] # or feed[1][0]
item_metadata = item[0] # or feed[1][0][0]
item_content = item[1] # or feed[1][0][1]
item_metadata[ 'id' ]
# => "2"
item_metadata[ 'url' ]
# => "https://example.org/second-item"
item_content
# => "This is a second item."
item = feed_items[1] # or feed[1][1]
item_metadata = item[0] # or feed[1][1][0]
item_content = item[1] # or feed[1][1][1]
item_metadata[ 'id' ]
# => "1"
item_metadata[ 'url' ]
# => "https://example.org/initial-post"
item_content
# => "Hello, world!"
...
Another example. Let’s try a podcast:
text =<<TXT
|>>>
comment: "This is a podcast feed. You can add..."
title: "The Record"
home_page_url: "http://therecord.co/"
feed_url: "http://therecord.co/feed.txt"
</>
id: "http://therecord.co/chris-parrish"
title: "Special #1 - Chris Parrish"
url: "http://therecord.co/chris-parrish"
summary: "Brent interviews Chris Parrish, co-host of The Record and one-half of Aged & Distilled."
published: 2014-05-09T14:04:00-07:00
attachments:
- url: "http://therecord.co/downloads/The-Record-sp1e1-ChrisParrish.m4a"
mime_type: "audio/x-m4a"
size_in_bytes: 89970236
duration_in_seconds: 6629
---
Chris has worked at [Adobe][1] and as a founder of Rogue Sheep, which won an Apple Design Award for Postage.
Chris's new company is Aged & Distilled with Guy English - which shipped [Napkin](2),
a Mac app for visual collaboration. Chris is also the co-host of The Record.
He lives on [Bainbridge Island][3], a quick ferry ride from Seattle.
[1]: http://adobe.com/
[2]: http://aged-and-distilled.com/napkin/
[3]: http://www.ci.bainbridge-isl.wa.us/
<<<|
TXT
feed = Feedtxt.parse( text )
pp feed
resulting in:
[{"comment"=>"This is a podcast feed. You can add...",
"title"=>"The Record",
"home_page_url"=>"http://therecord.co/",
"feed_url"=>"http://therecord.co/feed.txt"
},
[
[{"id"=>"http://therecord.co/chris-parrish",
"title"=>"Special #1 - Chris Parrish",
"url"=>"http://therecord.co/chris-parrish",
"summary"=>"Brent interviews Chris Parrish, co-host of The Record and...",
"published"=>2014-05-09 23:04:00 +0200,
"attachments"=>
[{"url"=>"http://therecord.co/downloads/The-Record-sp1e1-ChrisParrish.m4a",
"mime_type"=>"audio/x-m4a",
"size_in_bytes"=>89970236,
"duration_in_seconds"=>6629}]
},
"Chris has worked at [Adobe][1] and as a founder of Rogue Sheep..."
]
]
]
and use like:
feed_metadata = feed[0]
feed_items = feed[1]
feed_metadata[ 'title' ]
# => "The Record"
feed_metadata[ 'feed_url' ]
# => "http://therecord.co/feed.txt"
item = feed_items[0] # or feed[1][0]
item_metadata = item[0] # or feed[1][0][0]
item_content = item[1] # or feed[1][0][1]
item_metadata[ 'title' ]
# => "Special #1 - Chris Parrish"
item_metadata[ 'url' ]
# => "http://therecord.co/chris-parrish
item_content
# => "Chris has worked at [Adobe][1] and as a founder of Rogue Sheep..."
...
Note: Feed.TXT supports alternative formats / styles for meta data blocks.
For now YAML, JSON and INI style
are built-in and shipping with the feedtxt
gem.
To use a format-specific parser use:
Feedtxt::YAML.parse
Feedtxt::JSON.parse
Feedtxt::INI.parse
Note: Feedtxt.parse
will handle all formats auto-magically,
that is, it will check the text for the best matching (first)
feed begin marker
to find out what meta data format parser to use:
Format | FEED_BEGIN |
---|---|
YAML | \|>>> |
JSON | \|{ |
INI | [>>> |
Or use the built-in text pattern (regular expression) constants to find out:
Feedtxt::YAML::FEED_BEGIN
# => "^[ ]*\\|>>>+[ ]*$"
Feedtxt::JSON::FEED_BEGIN
# => "^[ ]*\\|{+[ ]*$"
Feedtxt::INI::FEED_BEGIN
# => "^[ ]*\\[>>>+[ ]*$"
|{
"title": "My Example Feed",
"home_page_url": "https://example.org/",
"feed_url": "https://example.org/feed.txt"
}/{
"id": "2",
"url": "https://example.org/second-item"
}-{
This is a second item.
}/{
"id": "1",
"url": "https://example.org/initial-post"
}-{
Hello, world!
}|
Note: Use |{
and }|
to begin and end your Feed.TXT.
Use }/{
for first or next item
and }-{
for meta blocks inside items.
(Source: feeds/spec/example.json.txt
)
[>>>
title = My Example Feed
home_page_url = https://example.org/
feed_url = https://example.org/feed.txt
</>
id = 2
url = https://example.org/second-item
---
This is a second item.
</>
id = 1
url = https://example.org/initial-post
---
Hello, world!
<<<]
or
[>>>
title: My Example Feed
home_page_url: https://example.org/
feed_url: https://example.org/feed.txt
</>
id: 2
url: https://example.org/second-item
---
This is a second item.
</>
id: 1
url: https://example.org/initial-post
---
Hello, world!
<<<]
(Source: feeds/spec/example.ini.txt
)
Note: Use [>>>
and <<<]
to begin and end your Feed.TXT.
Use </>
for first or next item
and ---
for meta blocks inside items.