Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Erbitte Anfaengerkritik

12 views
Skip to first unread message

Marc Haber

unread,
Feb 26, 2012, 5:03:40 AM2/26/12
to
Hallo,

ich möchte meinen Nagios-Config-Generator auf IPv6 erweitern und bei
dieser Gelegenheit noch einige Luxufeatures dazuimplementieren. Bisher
habe ich das mit perl und template-toolkit implementiert, habe mich
aber entschlossen, die neue Version "zum warmwerden" in ruby zu
programmieren.

Meine Arbeit erfordert im Moment sowieso, dass ich etwas firmer in
ruby werde. Außerdem finde ich den Ansatz, innerhalb der Templates
dieelbe Programmiersprache zu benutzen wie im eigentlichen Code,
erheblich eleganter als ständig zwischen perl und dieser komischen
Template-Toolkit-Sprache hin- und her zu wechseln.

Hier mein vorläufiger Code:

#!/usr/bin/ruby

require 'json'
require 'pp'
require 'erb'
require 'resolv'

# parse JSON input data

file = File.open("example.json", "r")
data = file.read
parsed = JSON.parse(data)
defaults = parsed["defaults"]
hosts = parsed["host"]

# templates

hostdefinitiontemplate = %q{
define host
use <%= hostdata["use"] %>
host_name <%= hostdata["name"] %>_<%= hostdata["thisip"]
%>
hostgroups <% last=hostdata["groups"].shift %><%= last
%><% hostdata["groups"].each do |group| %>, <%= group %><% end %>
alias <%= hostdata["name"] %> (<%=
hostdata["thisip"] %>)
address <%= hostdata["thisip"] %>
parents <%= hostdata["parents"] %>
}.gsub(/^ /, '')

# iterate through hosts

hosts.keys.each do |hostname|

# save us from writing "hosts[hostname]"
puts hostname.pretty_inspect if $DEBUG
hostdata=hosts[hostname]
puts hostdata.pretty_inspect if $DEBUG

# allow identical handling for host name and other properties
hostdata["name"] ||= hostname

# pull in defaults from defaults stanza
hostdata["use"] ||= defaults["use"]
hostdata["parents"] ||= defaults["parents"]
hostdata["groups"] = hostdata["services"]

# pull all host's IPs from DNS
hostdata["ip"] = []
dns = Resolv::DNS.new
dns.getresources(hostdata["name"],
Resolv::DNS::Resource::IN::A).collect do |r|
hostdata["ip"] << r.address
end
dns.getresources(hostdata["name"],
Resolv::DNS::Resource::IN::AAAA).collect do |r|
hostdata["ip"] << r.address
end
puts hostdata.pretty_inspect if $DEBUG

# expand variable once per IP address
hostdata["ip"].each do |ip|
hostdata["thisip"] = ip
template = ERB.new(hostdefinitiontemplate, 0, "%<>")
output = template.result(binding)
puts output
end
end

(leider sind die Zeilenumbrüche da hineingerutscht, das krieg ich
kurzfristig mit meinem Newsreader nicht anders hin; die Alternative
wäre das ganze Zeug mit | zu quoten, dann könnt Ihr kein cut&paste
mehr machen).

Hier sind Test-Eingabedaten:
$ cat example.json
{
"defaults" : {
"use": "mh-servers",
"hostcheck": "ping",
"hostgroups": [ "mh-servers" ],
"parents": [ "internet" ]
},

"host": {
"torres.zugschlus.de": {
"use": "bla",
"services": [ "ping", "smtp", "ssh" ],
"service": [
{
"name": "ping",
"target": [ "85.214.131.164" ]
}
],
"alias": ""
},
"q.bofh.de": {
"services": [ "ping", "ssh", "smtp", "http" ],
"alias": ""
},
"twoaddresses.zugschlus.de": {
"services": [ "ping", "ssh" ],
"alias": ""
}
}
}

Mein Zielsystem ist Debian squeeze, ich habe also nicht die
allerneuste Software zur Verfügung.

Nun meine Fragen:

Ich habe immer Schwierigkeiten, die Trennstelle zwischen dem
"eigentlichen Code" und der Template zu finden. Die beiden Extreme
sind natürlich nicht sinnvoll, aber ob ich hier (eine
Template-Expansion pro Hostdefinition) den richtigen Kompromiss
gefunden habe, lässt mich zweifeln

In den meisten ERB-Beispielen, die ich gefunden habe, wird eine
Extraklasse mit den Daten für die Template definiert. Ich vermute, in
so einem Setup kommt man in der Template ohne das ständige
'hostdata["foo"]' aus, und kann stattdessen einfach <%= foo %>
schreiben. Aber: Wie muss mein Code dafür aussehen?

Geht meine Konstruktion zum Bau einer kommagetrennten Liste
| <% last=hostdata["groups"].shift %><%= last %><% hostdata["groups"].each do |group| %>, <%= group %><% end %>
auch etwas eleganter?

Gibt es eine JSON-Library, deren "Pingeligkeit" man reduzieren kann?
In perl gibt es JON::XS->new->relaxed->decode, wo man Listen mit
Extrakommas abschließen kann, wo man Kommentare schreiben kann und
grundsätzlich etwas laxer mit der Syntax sein kann, ohne dass man
direkt einen Parse Error um die Ohren geworfen bekommt. Gibt es sowas
auch für Ruby? Kann man die Ruby-JSON-Library irgendwie dazu bewegen,
etwas genauere Fehlermeldungen zu produzieren, und mir wenigstens zu
sagen, in welcher Zeile der Eingabe der 'parse error near {'
aufgetreten ist?

In vielem Ruby-Code gibt es Konstruktionen wie object[:index] - hat
der Doppelpunkt eine besondere Bewandtnis?


Natürlich stehe ich auch stylistischen Kommentaren offen gegenüber,
ich habe noch nicht viel Gefühl für die Sprache.

Vielen Dank für Eure Mühe.

Grüße
Marc, hoffend, dass immer noch Leute mitlesen.
--
-------------------------------------- !! No courtesy copies, please !! -----
Marc Haber | " Questions are the | Mailadresse im Header
Mannheim, Germany | Beginning of Wisdom " | http://www.zugschlus.de/
Nordisch by Nature | Lt. Worf, TNG "Rightful Heir" | Fon: *49 621 72739834

Robert Klemme

unread,
Feb 26, 2012, 7:44:59 AM2/26/12
to
On 26.02.2012 11:03, Marc Haber wrote:
> Hallo,

Oh, es gibt noch Leben in der Gruppe!

> ich möchte meinen Nagios-Config-Generator auf IPv6 erweitern und bei
> dieser Gelegenheit noch einige Luxufeatures dazuimplementieren. Bisher
> habe ich das mit perl und template-toolkit implementiert, habe mich
> aber entschlossen, die neue Version "zum warmwerden" in ruby zu
> programmieren.

Sehr lobenswert!

> Meine Arbeit erfordert im Moment sowieso, dass ich etwas firmer in
> ruby werde. Außerdem finde ich den Ansatz, innerhalb der Templates
> dieelbe Programmiersprache zu benutzen wie im eigentlichen Code,
> erheblich eleganter als ständig zwischen perl und dieser komischen
> Template-Toolkit-Sprache hin- und her zu wechseln.
>
> Hier mein vorläufiger Code:
>
> #!/usr/bin/ruby
>
> require 'json'
> require 'pp'
> require 'erb'
> require 'resolv'
>
> # parse JSON input data
>
> file = File.open("example.json", "r")
> data = file.read
> parsed = JSON.parse(data)

Nutze besser die Block-Form von File.open um sicherzustellen, dass der
Filedeskriptor unter allen Umständen auch sauber geschlossen wird.
Außerdem liest man besser nicht erst in den Speicher (ineffizient)
sondern lässt den Parser gleicht den Stream lesen:

parsed = File.open("example.json") {|io| JSON.load(io)}

> defaults = parsed["defaults"]
> hosts = parsed["host"]
>
> # templates
>
> hostdefinitiontemplate = %q{
> define host
> use<%= hostdata["use"] %>
> host_name<%= hostdata["name"] %>_<%= hostdata["thisip"]
> %>
> hostgroups<% last=hostdata["groups"].shift %><%= last
> %><% hostdata["groups"].each do |group| %>,<%= group %><% end %>
> alias<%= hostdata["name"] %> (<%=
> hostdata["thisip"] %>)
> address<%= hostdata["thisip"] %>
> parents<%= hostdata["parents"] %>
> }.gsub(/^ /, '')

Warum änderst Du die Einrückung nachträglich?

> # iterate through hosts
>
> hosts.keys.each do |hostname|

Gleich über Schlüssel und Wert iterieren:

hosts.each do |hostname, hostdata|

> # save us from writing "hosts[hostname]"
> puts hostname.pretty_inspect if $DEBUG
> hostdata=hosts[hostname]

raus (s.o.)
Oder Du nutzt pastie, einen anderen öffentlichen Pastebin oder machst
einen Gist auf github daraus.
Für diesen Anwendungsfall würde ich wohl ERB gar nicht verwenden. Ich
finde das deutlich einfacher:

puts %Q[
define host
use #{ hostdata["use"] }
host_name #{ hostdata["name"] }_#{ hostdata["thisip"]}
hostgroups #{ hostdata["groups"].join(', ')}
alias #{ hostdata["name"] } (#{ hostdata["thisip"] })
address #{ hostdata["thisip"] }
parents #{ hostdata["parents"] }
]

> In den meisten ERB-Beispielen, die ich gefunden habe, wird eine
> Extraklasse mit den Daten für die Template definiert. Ich vermute, in
> so einem Setup kommt man in der Template ohne das ständige
> 'hostdata["foo"]' aus, und kann stattdessen einfach<%= foo %>
> schreiben. Aber: Wie muss mein Code dafür aussehen?

Du kannst es so machen:

Hostdata = Struct.new :host, :ip do
def bind
binding
end
end

hd = Hostdata.new "foo", "127.0.0.1"

erb = ERB.new "host = <%= host%>\nip = <%= ip %>\n"
erb.result(hd.bind)

Aber, wie gesagt: ERB ist m.E. Overkill.

> Geht meine Konstruktion zum Bau einer kommagetrennten Liste
> |<% last=hostdata["groups"].shift %><%= last %><% hostdata["groups"].each do |group| %>,<%= group %><% end %>
> auch etwas eleganter?

s.o.

> Gibt es eine JSON-Library, deren "Pingeligkeit" man reduzieren kann?
> In perl gibt es JON::XS->new->relaxed->decode, wo man Listen mit
> Extrakommas abschließen kann, wo man Kommentare schreiben kann und
> grundsätzlich etwas laxer mit der Syntax sein kann, ohne dass man
> direkt einen Parse Error um die Ohren geworfen bekommt. Gibt es sowas
> auch für Ruby? Kann man die Ruby-JSON-Library irgendwie dazu bewegen,
> etwas genauere Fehlermeldungen zu produzieren, und mir wenigstens zu
> sagen, in welcher Zeile der Eingabe der 'parse error near {'
> aufgetreten ist?

Weiß ich leider nicht. Ich würde aber sowieso eher mit dem strikten
Format arbeiten. Damit ist auf jeden Fall sichergestellt, dass das JSON
überall sauber verarbeitet wird. Wozu gibt es sonst Standards?

> In vielem Ruby-Code gibt es Konstruktionen wie object[:index] - hat
> der Doppelpunkt eine besondere Bewandtnis?

:index ist ein Symbol. Symbole sind jeweils nur ein Mal im Speicher,
egal wie oft Du sie referenzierst. Deshalb eignen sie sich gut für
feste Mengen von Schlüsseln.

> Natürlich stehe ich auch stylistischen Kommentaren offen gegenüber,
> ich habe noch nicht viel Gefühl für die Sprache.

Das kommt. Das Debuggen kann man auch so machen:

if $DEBUG
def debug
pp yield
end
else
def debug; end
end

Und dann

puts hostname.pretty_inspect if $DEBUG

->

debug { hostname }

Dann kannst Du in dem Block sogar komplizierte Berechnungen
unterbringen, die nur bei DEBUG auch tatsächlich ausgeführt werden und
so die normale Performance nicht beeinflussen.

> Vielen Dank für Eure Mühe.

Danichfür. Ich freue mich über jeden, der sich neu für Ruby entscheidet.

> Grüße
> Marc, hoffend, dass immer noch Leute mitlesen.

Ja, erstaunlich, nicht? ;-)

Viele Grüße

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Simon Krahnke

unread,
Feb 26, 2012, 2:06:55 PM2/26/12
to
* Marc Haber <mh+usene...@zugschl.us> (11:03) schrieb:

> file = File.open("example.json", "r")
> data = file.read

Wenn ich das richtig sehe, wird fiel nicht weiter verwendet, du kannst
also File.read("example.json").

> parsed = JSON.parse(data)

Oder gar: parsed = JSON.parse File.read("example.json")

Und dann kann vielleicht noch einen besseren Namen für parsed finden.

Mit denen ganzen Bibliotheken die du da benutzt kenen ich mich nicht
aus, also nix dazu.

> (leider sind die Zeilenumbrüche da hineingerutscht, das krieg ich
> kurzfristig mit meinem Newsreader nicht anders hin; die Alternative
> wäre das ganze Zeug mit | zu quoten, dann könnt Ihr kein cut&paste
> mehr machen).

Ha!

> Mein Zielsystem ist Debian squeeze, ich habe also nicht die
> allerneuste Software zur Verfügung.

:-)

> Nun meine Fragen:
>
> Ich habe immer Schwierigkeiten, die Trennstelle zwischen dem
> "eigentlichen Code" und der Template zu finden. Die beiden Extreme
> sind natürlich nicht sinnvoll, aber ob ich hier (eine
> Template-Expansion pro Hostdefinition) den richtigen Kompromiss
> gefunden habe, lässt mich zweifeln

Ich finde das eigentlich immer einfach, keine Business-Logik im
Template, sondern nur Präsentation.

> In den meisten ERB-Beispielen, die ich gefunden habe, wird eine
> Extraklasse mit den Daten für die Template definiert. Ich vermute, in
> so einem Setup kommt man in der Template ohne das ständige
> 'hostdata["foo"]' aus, und kann stattdessen einfach <%= foo %>
> schreiben. Aber: Wie muss mein Code dafür aussehen?

Ich hab mal ein Web-Template-System um ERB ergänzt, das sieht so aus:

,----[ template.rb ]
| require 'erb'
|
| class Template
| attr_accessor :params, :top, :erb
|
| def initialize input, top, params
| @params, @top, @erb = params, top, ERB.new(input, $SAFE, '>')
| end
|
| def input
| @erb.src
| end
|
| def input= input, safe = $SAFE
| if input.respond_to? :read
| input = input.read
| end
| @erb = ERB.new input, safe, '>'
| end
|
| def make_binding
| b = if @top then @top.get_binding else nil.send :binding end
| @params.each_pair do | key, value |
| id = value.object_id
| eval "#{key} = ObjectSpace._id2ref(#{id})", b
| end
| id = object_id
| eval "template = ObjectSpace._id2ref(#{id})", b
| b
| end
|
| def run
| @erb.run(make_binding)
| end
|
| def result
| @erb.result(make_binding)
| end
|
| def sub input, params
| t = dup
| t.input = input
| t.params = @params.merge(params) if params
| t
| end
| end
`----

Und ein Template sieht so aus:

,----[ sitemap.rxml ]
| <?xml version="1.0" encoding="utf-8" ?>
| <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
| <% for set in sets %>
| <url>
| <loc><%= baseurl %><%= if set.url == '.' then '' else set.url end%></loc>
| <changefreq>monthly</changefreq>
| <% if(priority = set.params['priority']) %>
| <priority><%= priority %></priority>
| <% end %>
| </url>
| <% end %>
| </urlset>
`----


> Geht meine Konstruktion zum Bau einer kommagetrennten Liste
>| <% last=hostdata["groups"].shift %><%= last %><% hostdata["groups"].each do |group| %>, <%= group %><% end %>
> auch etwas eleganter?

<%= hostdata["groups"].join(', ') %> ?

> In vielem Ruby-Code gibt es Konstruktionen wie object[:index] - hat
> der Doppelpunkt eine besondere Bewandtnis?

Das sind Symbole. Sowas wie String#intern in Java. :index ist immer das
gleiche Symbol, das gleiche Objekt, egal wie oft das im Source-Code
vorkommt, kann also ganz einfach verglichen werden.

> Natürlich stehe ich auch stylistischen Kommentaren offen gegenüber,
> ich habe noch nicht viel Gefühl für die Sprache.

Du solltest mal mein erstes Ruby-Programm sehen.

> Marc, hoffend, dass immer noch Leute mitlesen.

mfg, simon .... leere gruppen lesen kostet nichts

Marc Haber

unread,
Feb 26, 2012, 4:16:38 PM2/26/12
to
Robert Klemme <short...@googlemail.com> wrote:
>On 26.02.2012 11:03, Marc Haber wrote:
>Nutze besser die Block-Form von File.open um sicherzustellen, dass der
>Filedeskriptor unter allen Umständen auch sauber geschlossen wird.
>Außerdem liest man besser nicht erst in den Speicher (ineffizient)
>sondern lässt den Parser gleicht den Stream lesen:
>
>parsed = File.open("example.json") {|io| JSON.load(io)}

Sieht schon sehr wie Ruby aus. Muss ich erstmal nachlesen, was das
macht ;)

>> hostdefinitiontemplate = %q{
>> define host
>> use<%= hostdata["use"] %>
>> host_name<%= hostdata["name"] %>_<%= hostdata["thisip"]
>> %>
>> hostgroups<% last=hostdata["groups"].shift %><%= last
>> %><% hostdata["groups"].each do |group| %>,<%= group %><% end %>
>> alias<%= hostdata["name"] %> (<%=
>> hostdata["thisip"] %>)
>> address<%= hostdata["thisip"] %>
>> parents<%= hostdata["parents"] %>
>> }.gsub(/^ /, '')
>
>Warum änderst Du die Einrückung nachträglich?

Weil das in dem ergoogelten Beispiel so stand ;)

>> (leider sind die Zeilenumbrüche da hineingerutscht, das krieg ich
>> kurzfristig mit meinem Newsreader nicht anders hin; die Alternative
>> wäre das ganze Zeug mit | zu quoten, dann könnt Ihr kein cut&paste
>> mehr machen).
>
>Oder Du nutzt pastie, einen anderen öffentlichen Pastebin oder machst
>einen Gist auf github daraus.

Das erfordert, dass derjenige, der mir antwortet, beim Antworten
online ist. Da ich selbst sehr viel Usenet im Zug lese, ...

>> Ich habe immer Schwierigkeiten, die Trennstelle zwischen dem
>> "eigentlichen Code" und der Template zu finden. Die beiden Extreme
>> sind natürlich nicht sinnvoll, aber ob ich hier (eine
>> Template-Expansion pro Hostdefinition) den richtigen Kompromiss
>> gefunden habe, lässt mich zweifeln
>
>Für diesen Anwendungsfall würde ich wohl ERB gar nicht verwenden. Ich
>finde das deutlich einfacher:
>
>puts %Q[
>define host
> use #{ hostdata["use"] }
> host_name #{ hostdata["name"] }_#{ hostdata["thisip"]}
> hostgroups #{ hostdata["groups"].join(', ')}
> alias #{ hostdata["name"] } (#{ hostdata["thisip"] })
> address #{ hostdata["thisip"] }
> parents #{ hostdata["parents"] }
>]

Für diesen Anfang, ja, aber das wird später sicher noch komplexer.

>> In den meisten ERB-Beispielen, die ich gefunden habe, wird eine
>> Extraklasse mit den Daten für die Template definiert. Ich vermute, in
>> so einem Setup kommt man in der Template ohne das ständige
>> 'hostdata["foo"]' aus, und kann stattdessen einfach<%= foo %>
>> schreiben. Aber: Wie muss mein Code dafür aussehen?
>
>Du kannst es so machen:
>
>Hostdata = Struct.new :host, :ip do
> def bind
> binding
> end
> end
>
>hd = Hostdata.new "foo", "127.0.0.1"
>
>erb = ERB.new "host = <%= host%>\nip = <%= ip %>\n"
>erb.result(hd.bind)

Das bedeutet aber, dass ich in der Definition des Hostdata-Objekts
wissen muss, welche Keys ich verwenden werde. Das wäre dann eine
dritte Stelle, wo ich das reinschreiben muss. Ich sehe den Vorteil
nicht.

>Aber, wie gesagt: ERB ist m.E. Overkill.

Gesetzt den Fall, das würde noch komplexer...

>> Geht meine Konstruktion zum Bau einer kommagetrennten Liste
>> |<% last=hostdata["groups"].shift %><%= last %><% hostdata["groups"].each do |group| %>,<%= group %><% end %>
>> auch etwas eleganter?
>
>s.o.

hostdata["groups"].join(', ') geht, ja, danke.

>> Gibt es eine JSON-Library, deren "Pingeligkeit" man reduzieren kann?
>> In perl gibt es JON::XS->new->relaxed->decode, wo man Listen mit
>> Extrakommas abschließen kann, wo man Kommentare schreiben kann und
>> grundsätzlich etwas laxer mit der Syntax sein kann, ohne dass man
>> direkt einen Parse Error um die Ohren geworfen bekommt. Gibt es sowas
>> auch für Ruby? Kann man die Ruby-JSON-Library irgendwie dazu bewegen,
>> etwas genauere Fehlermeldungen zu produzieren, und mir wenigstens zu
>> sagen, in welcher Zeile der Eingabe der 'parse error near {'
>> aufgetreten ist?
>
>Weiß ich leider nicht. Ich würde aber sowieso eher mit dem strikten
>Format arbeiten. Damit ist auf jeden Fall sichergestellt, dass das JSON
>überall sauber verarbeitet wird. Wozu gibt es sonst Standards?

Im konkreten Fall nehme ich JSON eh nur, weil es dafür einen
brauchbaren fertigen Parser gibt. Mir wäre es an dieser Stelle
wichtiger, meine Konfiguration auch kommentieren zu können.

_Eigentlich_ hätte ich ja gerne einen Parser für das
ISC-Konfigfileformat (bind9, inn, dhcpd)

>> In vielem Ruby-Code gibt es Konstruktionen wie object[:index] - hat
>> der Doppelpunkt eine besondere Bewandtnis?
>
>:index ist ein Symbol. Symbole sind jeweils nur ein Mal im Speicher,
>egal wie oft Du sie referenzierst. Deshalb eignen sie sich gut für
>feste Mengen von Schlüsseln.

object["index"] frisst also bei jeder Verwendung Speicher,
object[:index] nur einmal? Wie schreibe ich das in JSON, dass
JSON.load die Werte als Symbol einliest?

>> Natürlich stehe ich auch stylistischen Kommentaren offen gegenüber,
>> ich habe noch nicht viel Gefühl für die Sprache.
>
>Das kommt. Das Debuggen kann man auch so machen:
>
>if $DEBUG
> def debug
> pp yield
> end
>else
> def debug; end
>end
>
>Und dann
>
> puts hostname.pretty_inspect if $DEBUG
>
>->
>
> debug { hostname }

Was bedeutet "pp yield"?

Grüße
Marc

Marc Haber

unread,
Feb 26, 2012, 4:20:11 PM2/26/12
to
Simon Krahnke <over...@gmx.li> wrote:
>* Marc Haber <mh+usene...@zugschl.us> (11:03) schrieb:
>> file = File.open("example.json", "r")
>> data = file.read
>
>Wenn ich das richtig sehe, wird fiel nicht weiter verwendet, du kannst
>also File.read("example.json").
>
>> parsed = JSON.parse(data)
>
>Oder gar: parsed = JSON.parse File.read("example.json")

Hat das Vorteile gegenüber der von Robert vorgeschlagenen Block-Form?

>Und dann kann vielleicht noch einen besseren Namen für parsed finden.

Und der könnte lauten?

>> Mein Zielsystem ist Debian squeeze, ich habe also nicht die
>> allerneuste Software zur Verfügung.
>
>:-)

Naja, natürlich könnte ich backporten, aber ruby zu backporten wird
sicher nicht einfacher sein als python oder perl, also lieber Finger
weg, wenn es sich vermeiden lässt.

>> Nun meine Fragen:
>>
>> Ich habe immer Schwierigkeiten, die Trennstelle zwischen dem
>> "eigentlichen Code" und der Template zu finden. Die beiden Extreme
>> sind natürlich nicht sinnvoll, aber ob ich hier (eine
>> Template-Expansion pro Hostdefinition) den richtigen Kompromiss
>> gefunden habe, lässt mich zweifeln
>
>Ich finde das eigentlich immer einfach, keine Business-Logik im
>Template, sondern nur Präsentation.

Und genau diese Stelle zu finden fordert mich schon bei perl/TT immer
heraus.
Da hast Du mich jetzt abgehängt. Gibt es das auch kommentiert?

>> Geht meine Konstruktion zum Bau einer kommagetrennten Liste
>>| <% last=hostdata["groups"].shift %><%= last %><% hostdata["groups"].each do |group| %>, <%= group %><% end %>
>> auch etwas eleganter?
>
><%= hostdata["groups"].join(', ') %> ?

Geht.

>> In vielem Ruby-Code gibt es Konstruktionen wie object[:index] - hat
>> der Doppelpunkt eine besondere Bewandtnis?
>
>Das sind Symbole. Sowas wie String#intern in Java.

Volltreffer, Java kann ich auch nicht. Vergleich mal mit C oder perl,
von mir aus auch mit Pascal ;)

> :index ist immer das
>gleiche Symbol, das gleiche Objekt, egal wie oft das im Source-Code
>vorkommt, kann also ganz einfach verglichen werden.

"einfach" im Sinne von "billiger als ein Stringvergleich?

Grüße
Marc

Simon Krahnke

unread,
Feb 27, 2012, 12:49:56 AM2/27/12
to
* Marc Haber <mh+usene...@zugschl.us> (22:20) schrieb:

>> Oder gar: parsed = JSON.parse File.read("example.json")
>
> Hat das Vorteile gegenüber der von Robert vorgeschlagenen Block-Form?

Nein, nimm Roberts Block-Form.

>> Und dann kann vielleicht noch einen besseren Namen für parsed finden.
>
> Und der könnte lauten?

Öhm, die parsed-Repräsentation ist dann doch die einzige Form der Daten
die es gibt, also wie immer du deinen Daten nennen willst.

>>| def make_binding
>>| b = if @top then @top.get_binding else nil.send :binding end
>>| @params.each_pair do | key, value |
>>| id = value.object_id
>>| eval "#{key} = ObjectSpace._id2ref(#{id})", b
>>| end
>>| id = object_id
>>| eval "template = ObjectSpace._id2ref(#{id})", b
>>| b
>>| end

Bindings sind halt Rubys Weg lokale Variablen für eval bereitzuhalten.
Das Objekt in dem man binding aufruft ist in dem Binding dann self.

Ich hatte damals das Problem, in den String für eval das gleiche Objekt
verwenden zu wollen, das ich draußen schon habe. Mein Weg war halt über
Nummern, die object_ids zu gehen, das ist einfach der Pointer den Ruby
intern benutzt. Und mit ObjectSpace._id2ref macht man da halt wieder ein
Object daraus. Das Grundübel ist hier halt der String, vielleicht weiß
Robert ja was dazu.

@params ist einfach der Hash der an der Template-Objekt übergeben wurde,
wo dann die lokalen Variablen drin stehen.

>> Und ein Template sieht so aus:
>>
>>,----[ sitemap.rxml ]
>>| <?xml version="1.0" encoding="utf-8" ?>
>>| <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
>>| <% for set in sets %>

Und eine dieser lokalen Variablen ist halt sets, eine Liste von
Set-Objekten, die die grundlegende Datenstruktur meines Systems war, in
diesem Beispiel (das eine sitemap.xml generiert) jede Seite die in der
Sitemap auftauchen soll.

>>| <url>
>>| <loc><%= baseurl %><%= if set.url == '.' then '' else set.url end%></loc>
>>| <changefreq>monthly</changefreq>
>>| <% if(priority = set.params['priority']) %>
>>| <priority><%= priority %></priority>
>>| <% end %>
>>| </url>
>>| <% end %>
>>| </urlset>
>>`----
>
> Da hast Du mich jetzt abgehängt. Gibt es das auch kommentiert?

Tja, das halt nie zum Veröffentlichen gemacht. Ich bin froh, das ich das
noch selbst kapier.

>>> In vielem Ruby-Code gibt es Konstruktionen wie object[:index] - hat
>>> der Doppelpunkt eine besondere Bewandtnis?
>>
>> Das sind Symbole. Sowas wie String#intern in Java.
>
> Volltreffer, Java kann ich auch nicht. Vergleich mal mit C oder perl,
> von mir aus auch mit Pascal ;)

Aha, Programmiersprachen deren Namen mit P anfängt benutze ich nicht.

>> :index ist immer das
>> gleiche Symbol, das gleiche Objekt, egal wie oft das im Source-Code
>> vorkommt, kann also ganz einfach verglichen werden.
>
> "einfach" im Sinne von "billiger als ein Stringvergleich?

Ja, es ist halt ein Pointer-Vergleich.

mfg, simon .... l

Robert Klemme

unread,
Feb 28, 2012, 1:51:58 AM2/28/12
to

On 26.02.2012 22:16, Marc Haber wrote:
> Robert Klemme<short...@googlemail.com> wrote:
>> On 26.02.2012 11:03, Marc Haber wrote:
>> Nutze besser die Block-Form von File.open um sicherzustellen, dass der
>> Filedeskriptor unter allen Umständen auch sauber geschlossen wird.
>> Außerdem liest man besser nicht erst in den Speicher (ineffizient)
>> sondern lässt den Parser gleicht den Stream lesen:
>>
>> parsed = File.open("example.json") {|io| JSON.load(io)}
>
> Sieht schon sehr wie Ruby aus. Muss ich erstmal nachlesen, was das
> macht ;)

Block Form von File.open öffnet die Datei und übergibt das offene
File-Objekt. Wenn der Block - wie auch immer (z.B. mit Exception) -
verlassen wird wird das File geschlossen.

Benutzung
http://blog.rubybestpractices.com/posts/rklemme/001-Using_blocks_for_Robustness.html

Und wie man sowas selber macht
http://blog.rubybestpractices.com/posts/rklemme/002_Writing_Block_Methods.html

>> Warum änderst Du die Einrückung nachträglich?
>
> Weil das in dem ergoogelten Beispiel so stand ;)

Schlechte Begründung. ;-)

> Das bedeutet aber, dass ich in der Definition des Hostdata-Objekts
> wissen muss, welche Keys ich verwenden werde. Das wäre dann eine
> dritte Stelle, wo ich das reinschreiben muss. Ich sehe den Vorteil
> nicht.

Du hattest danach gefragt. :-)

>>> Gibt es eine JSON-Library, deren "Pingeligkeit" man reduzieren kann?
>>> In perl gibt es JON::XS->new->relaxed->decode, wo man Listen mit
>>> Extrakommas abschließen kann, wo man Kommentare schreiben kann und
>>> grundsätzlich etwas laxer mit der Syntax sein kann, ohne dass man
>>> direkt einen Parse Error um die Ohren geworfen bekommt. Gibt es sowas
>>> auch für Ruby? Kann man die Ruby-JSON-Library irgendwie dazu bewegen,
>>> etwas genauere Fehlermeldungen zu produzieren, und mir wenigstens zu
>>> sagen, in welcher Zeile der Eingabe der 'parse error near {'
>>> aufgetreten ist?
>>
>> Weiß ich leider nicht. Ich würde aber sowieso eher mit dem strikten
>> Format arbeiten. Damit ist auf jeden Fall sichergestellt, dass das JSON
>> überall sauber verarbeitet wird. Wozu gibt es sonst Standards?
>
> Im konkreten Fall nehme ich JSON eh nur, weil es dafür einen
> brauchbaren fertigen Parser gibt. Mir wäre es an dieser Stelle
> wichtiger, meine Konfiguration auch kommentieren zu können.

Dann nimm doch Ruby-Code. Du kannst mit recht wenig Aufwand z.B. sowas
machen

configuration {
# das ist der Rechner, um den es geht
:host => "foo.bar",

# Den gibt es in diesen Domains
:domains => [".bar", ".de"],

# das ist natürlich ein total blödsinniges
# Beispiel
}

> _Eigentlich_ hätte ich ja gerne einen Parser für das
> ISC-Konfigfileformat (bind9, inn, dhcpd)

Hm, hast Du mal geschaut, ob es ein Gem gibt, das das kann? RAA ist
auch ein guter Platz zum Suchen
http://raa.ruby-lang.org/search.rhtml?search=bind

>>> In vielem Ruby-Code gibt es Konstruktionen wie object[:index] - hat
>>> der Doppelpunkt eine besondere Bewandtnis?
>>
>> :index ist ein Symbol. Symbole sind jeweils nur ein Mal im Speicher,
>> egal wie oft Du sie referenzierst. Deshalb eignen sie sich gut für
>> feste Mengen von Schlüsseln.
>
> object["index"] frisst also bei jeder Verwendung Speicher,

Ja und nein: es gibt zwar jedes Mal ein neues Objekt, aber der
eigentliche String ist nur einmal da wg. copy on write.

> object[:index] nur einmal? Wie schreibe ich das in JSON, dass
> JSON.load die Werte als Symbol einliest?

Vermutlich nicht.

>>> Natürlich stehe ich auch stylistischen Kommentaren offen gegenüber,
>>> ich habe noch nicht viel Gefühl für die Sprache.
>>
>> Das kommt. Das Debuggen kann man auch so machen:
>>
>> if $DEBUG
>> def debug
>> pp yield
>> end
>> else
>> def debug; end
>> end
>>
>> Und dann
>>
>> puts hostname.pretty_inspect if $DEBUG
>>
>> ->
>>
>> debug { hostname }
>
> Was bedeutet "pp yield"?

pp ist wie p nur dass größere Strukturen geschachtelt ausgegeben werden.
yield ruft den Block auf, der der Methode debug übergeben wurde.

Bist Du schon eins der Tutorials auf http://www.ruby-lang.org/de/
durchgegangen? Insbesondere "Programming Ruby" ist gut, wenn auch etwas
veraltet und in Englisch.

http://home.vrweb.de/~juergen.katins/ruby/buch/index.html

Bis dann

Marc Haber

unread,
Feb 28, 2012, 6:33:41 AM2/28/12
to
Robert Klemme <short...@googlemail.com> wrote:
>On 26.02.2012 22:16, Marc Haber wrote:
>> Sieht schon sehr wie Ruby aus. Muss ich erstmal nachlesen, was das
>> macht ;)
>
>Block Form von File.open öffnet die Datei und übergibt das offene
>File-Objekt. Wenn der Block - wie auch immer (z.B. mit Exception) -
>verlassen wird wird das File geschlossen.

Danke, überzeugt.

>>> Warum änderst Du die Einrückung nachträglich?
>>
>> Weil das in dem ergoogelten Beispiel so stand ;)
>
>Schlechte Begründung. ;-)

Aber die einzige die ich habe ;)

>> Im konkreten Fall nehme ich JSON eh nur, weil es dafür einen
>> brauchbaren fertigen Parser gibt. Mir wäre es an dieser Stelle
>> wichtiger, meine Konfiguration auch kommentieren zu können.
>
>Dann nimm doch Ruby-Code.

Keine Alternative, JSON gibt es für meinen existierenden
Konfigfilegenerator schon an einigen Stellen. Das anzupassen dürfte
signifikant einfacher sein.

>> _Eigentlich_ hätte ich ja gerne einen Parser für das
>> ISC-Konfigfileformat (bind9, inn, dhcpd)
>
>Hm, hast Du mal geschaut, ob es ein Gem gibt, das das kann?

Das würde mich sehr wundern; sowas gibt es nichtmal im CPAN.

> RAA ist
>auch ein guter Platz zum Suchen
>http://raa.ruby-lang.org/search.rhtml?search=bind

"parse Option and Config-file: The library that parse Option and
Config-file. Japanese document only." Allerliebst ;)

>>> Das kommt. Das Debuggen kann man auch so machen:
>>>
>>> if $DEBUG
>>> def debug
>>> pp yield
>>> end
>>> else
>>> def debug; end
>>> end
>>>
>>> Und dann
>>>
>>> puts hostname.pretty_inspect if $DEBUG
>>>
>>> ->
>>>
>>> debug { hostname }
>>
>> Was bedeutet "pp yield"?
>
>pp ist wie p nur dass größere Strukturen geschachtelt ausgegeben werden.
> yield ruft den Block auf, der der Methode debug übergeben wurde.
>
>Bist Du schon eins der Tutorials auf http://www.ruby-lang.org/de/
>durchgegangen?

Nein, so weit bin ich noch nicht. Ich brauchte am Wochenende erstmal
Ergebnisse. Das ist zugegebenermaßen eine seltsame herangehensweise,
aber ich hab im Moment an zu vielen Baustellen zu viel um die Ohren um
in Ruhe zum Tutorials Lesen zu kommen.

Oliver Cromm

unread,
Feb 29, 2012, 5:29:08 PM2/29/12
to
* Simon Krahnke:

> Bindings sind halt Rubys Weg lokale Variablen für eval bereitzuhalten.
> Das Objekt in dem man binding aufruft ist in dem Binding dann self.

Nun war ich überrascht, eval in einem mit "Anfänger" markierten
Thread erwähnt zu finden.

Ich bin kein professioneller Programmierer, aber ich benutze nun
seit über 10 Jahren hauptsächlich Ruby, wenn ich was programmiere,
habe aber nie richtig kapiert, wie eval arbeitet und wozu es gut
sein soll. Verpasse ich da was wichtiges oder ist es gut möglich,
daß das für meine Zwecke keine sonderlichen Vorteil bringt?

--
Pentiums melt in your PC, not in your hand.

Simon Krahnke

unread,
Mar 2, 2012, 2:46:02 AM3/2/12
to
* Oliver Cromm <lispa...@crommatograph.info> (2012-02-29) schrieb:

>* Simon Krahnke:
>
>> Bindings sind halt Rubys Weg lokale Variablen für eval bereitzuhalten.
>> Das Objekt in dem man binding aufruft ist in dem Binding dann self.
>
> Nun war ich überrascht, eval in einem mit "Anfänger" markierten
> Thread erwähnt zu finden.

Na ja, eval ist Core-Feature von Ruby als dynamische Sprache.

> Ich bin kein professioneller Programmierer, aber ich benutze nun
> seit über 10 Jahren hauptsächlich Ruby, wenn ich was programmiere,
> habe aber nie richtig kapiert, wie eval arbeitet und wozu es gut
> sein soll. Verpasse ich da was wichtiges oder ist es gut möglich,
> daß das für meine Zwecke keine sonderlichen Vorteil bringt?

Eval in seiner Grundfunktion führt einfach den übergebenden String als
Ruby-Code aus. Mit seinem zweiten Argument kann man ihm ein Binding
übergeben.

Nett, wenn man sowas wie IRB oder ein Template-System schreibt, oder
letzteres benutzt wie in meinen Beispiel ERB. Aber in einfachen
Skripten ohne usergenerierten Code braucht man eval wohl nie.

mfg, simon .... l
0 new messages