integrity 0.1.10 + rack 1.0.0 + thin 1.2.2 + sinatra 0.9.1.3 0

Posted by fwoeck
on Friday, May 29

Ich hoffe, dass sich die Aktualität dieses Artikels bald erledigt: kurz gefasst, die aktuelle gem-Version von Thin basiert auf rack 1.0.0, die aktuelle integrity-Version auf sinatra 1.9.1.1, welches selbst rack 0.9.x benötigt. Wenn man rack oder Thin oder beide updated, entstehen ein paar Probleme.

Dies ist – soweit ich es jetzt noch zusammenbekomme – wie ich auf meinem Ubuntu-Testserver eine (vermutlich) lauffähige Version zusammengefrickelt habe:

Eine sinatra-Version von SR (s.u.) clonen

Von http://github.com/sr/sinatra/tree/layout-local das git://github.com/sr/sinatra.git clonen und den branch layout-local auschecken:

> git checkout -b layout-local
> vi lib/sinatra/base.rb

dort musste ich für mein REE-1.8.6… die Zeile #46:

inject(0) { |len, part| len + part.bytesize }.to_s
gegen
inject(0) { |len, part| len + part.length }.to_s
ersetzen. Das ist natürlich nicht das Gleiche …

Anschließend noch ein

> rake install

Die letzte integrity-Version clonen

Von http://github.com/integrity/integrity/tree/master den git://github.com/integrity/integrity.git clonen:

> git clone git://github.com/integrity/integrity.git
> cd integrity
> vi integrity.gemspec  # 0.9.1.1 > 0.9.1.3
> gem uninstall integrity
> gem build integrity.gemspec 
> gem install integrity-0.1.10.gem

In der .gemspec muss die Abhängigkeit auf die Version 0.9.1.3 gesetzt werden, damit integrity auch die aktuelle sinatra-Version aufruft.

Weblinks

  1. irclogger.com/integrity/2009-05-19
  2. github.com/integrity/integrity/tree/master
  3. github.com/sr/sinatra/tree/layout-local
  4. http://integrityapp.com/

Stream-Server in drei Zeilen mit rack 0

Posted by fwoeck
on Friday, January 23

Einen minimalistischen Webservice mit rack zu bauen ist fast trivial. Man benötigt ein Objekt, dass auf die Methode call(env) mit einem Array aus drei Elementen reagiert, wie z.B. diesem hier:

[200, {"Content-Type"=>"text/plain"}, "Moooh!"]

Dieses in der richtigen Weise mit einem Webserver an rack gebunden, gibt einen funktionsfähigen Dienst:

1
2
3
4
5
6
7
8
require 'thin'
require 'rack'

class App
  def call(env)
    [200, { 'Content-Type' => 'text/plain' }, 'Moooh!']
  end
end
>> app = App.new
>> app.call(1)
=> [200, {"Content-Type"=>"text/plain"}, "Moooh!"]
>> Rack::Handler::Thin.run(app, :Port => 1234)
>> Thin web server (v1.0.0 codename That's What She Said)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:1234, CTRL+C to stop

Und voila: die Assetfunktion von mephisto funktioniert nach dem letzten Update nicht mehr, sonst gäb’s hier ein Bild. So muss man sich vorstellen, man gäbe im Firefox als URL

http://127.0.0.1:1234

ein und bekäme ein freundliches ‘Moooh!’

Da Proc-Schnispel ein eingebautes call haben ginge es auch so:

1
2
3
app = Proc.new do |env|
  [200, { 'Content-Type' => 'text/plain' }, 'Moooh!']
end

File-Streaming

Als letzter Parameter des Arrays (hier “Moooh!”) kann jedes Objekt dienen, dass each implementiert. Auf diesem Wege kann man recht einfach z.B. einen File-Streamer schreiben:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
require 'thin'
require 'rack'

class StreamingFile
  def initialize(file)
    @file = file
  end

  def length
    File.size(@file)
  end

  def last_modified
    File.mtime(@file).rfc822
  end

  def each
    File.open(@file, "r") do |file|
      while part = file.read(8192)
        yield part
      end
    end
  end
end

file = StreamingFile.new('/var/www/html/test.mp3')

app = Proc.new do |env|
  [200, { 'Content-Type' => 'audio/mp3', 'Content-Length' => file.length.to_s }, file]
end

Rack::Handler::Thin.run(app, :Port => 1234)

Env

Im env-Parameter stehen übrigens die Headerdaten des Clients (HTTP_USER_AGENT etc.) in einem Hash zusammengetragen und zur Auswertung bereit.

Unix Pipes

So etwas funktioniert auch mit Pipes. Via STDIN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
require 'thin'
require 'rack'

class StreamingFile
  def initialize(file)
    @file = file
  end

  def each
    while part = @file.read(8192)
      yield part
    end
  end
end

file = StreamingFile.new(STDIN)

app = Proc.new do |env|
  [200, { 'Content-Type' => 'application/octet-stream' }, file]
end

Rack::Handler::Thin.run(app, :Port => 1234)

kann man z.B. ein lokales tar-Archiv-streamen:

tar cz /home/me | ruby stream.rb

Cool?

Cool! Mehr dazu hier: danwebb.net