123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674 |
- require 'schild/version'
- require 'sequel'
- ##
- # === Schild Basis-Modul
- # Das Schild Modul, das alle Klassen für die Datenbankanbindung bereitstellt.
- # Die zur Verfügung gestellten Klassen entsprechen dabei den Schild-Tabellen,
- # und den damit verbundenen Assoaziationen.
- #
- # Neben dem Zugriff aus Daten innerhalb einer Tabelle kann über die Schild-Klassen
- # auch auf weitere verknüpfte Tabellen zugegriffen werden. Dazu ein Beispiel:
- #
- # Schueler[0].name
- # => "Olsen"
- #
- # Schueler[0].abschnitte[0].jahr
- # => 2017
- #
- # Im ersten Beispiel wurde auf den ersten Schüler in der Datenbank ([0]) zugegriffen,
- # und der `name` ausgelesen.
- #
- # Im zweiten Beispiel wird auf den gleichen Schüler zugegriffen und dessen Abschnitt-Tabelle
- # ebenfalls abgefragt. D.h. es wird bereits auf die Abschnitte-Tabelle zugegriffen
- # und automatisch nur Abschnitte zurückgegeben, die zum Schüler gehören. Aus
- # dieser Menge wird ebenfalls der erste Abschnitt gewählt ([0]).
- #
- # Auf diesem Weg können beliebig verschachtelte Anfragen gestartet werden.
- #
- # Es wird empfohlen statt Schild das Modul SchildErweitert einzubinden. Es
- # beinhaltet neben den Assoaziationen auch weitere Methoden.
- # Weitere Erläuterungen dazu weiter im Modul.
- module Schild
- Sequel::Model.plugin :tactical_eager_loading
- # @db ist die Datenbank-Verbindung. Alle Daten können über diese Variable abgerufen werden
- @db = Sequel.connect("#{ENV['S_ADAPTER']}://#{ENV['S_HOST']}/#{ENV['S_DB']}?user=#{ENV['S_USER']}&password=#{ENV['S_PASSWORD']}&zeroDateTimeBehavior=convertToNull")
- begin
- retries ||= 0
- @db.test_connection
- rescue
- puts "Verbindung zum Server konnte nicht hergestellt werden"
- puts "#{retries += 1}. Verbindungsversuch in 5s (max 50 Versuche)"
- puts "Sie können mit Strg-c abbrechen."
- sleep 5
- retry if retries < 50
- end
- @db.extension(:freeze_datasets)
- @db.extension(:connection_validator)
- @db.extension(:null_dataset)
- # gibt die Datenbank zurück
- def self.db
- @db
- end
- # Stellt die Schüler-Tabelle mit Assoziationen bereit.
- # Die aufgeführten Instanzmethoden sind ausschließlich Assoaziationen.
- #
- # Tabellenname: schueler
- class Schueler < Sequel::Model(:schueler)
- # @!method fachklasse
- # @return [Fachklasse]
- many_to_one :fachklasse, :class => :Fachklasse
- # @!method abschnitte
- # @return [Array<Abschnitt>]
- one_to_many :abschnitte, :class => :Abschnitt
- # @!method bk_abschluss
- # @return [BKAbschluss]
- one_to_one :bk_abschluss, :class => :BKAbschluss
- # @!method bk_abschluss_leistungen
- # @return [Array<BKAbschlussFach>]
- one_to_many :bk_abschluss_leistungen, :class => :BKAbschlussFach
- # @!method abi_abschluss
- # @return [AbiAbschluss]
- one_to_one :abi_abschluss, :class => :AbiAbschluss
- # @!method abi_abschluss_leistungen
- # @return [Array<AbiAbschlussFach>]
- one_to_many :abi_abschluss_leistungen, :class => :AbiAbschlussFach
- # @!method fhr_abschluss
- # @return [FHRAbschluss]
- one_to_one :fhr_abschluss, :class => :FHRAbschluss
- # @!method fhr_abschluss_leistungen
- # @return [Array<FHRAbschlussFach>]
- one_to_many :fhr_abschluss_leistungen, :class => :FHRAbschlussFach
- # @!method vermerke
- # @return [Array<Vermerk>]
- one_to_many :vermerke, :class => :Vermerk
- # @!method schuelerfoto
- # @return [Schuelerfoto]
- one_to_one :schuelerfoto, :class => :Schuelerfoto
- # @!method sprachenfolgen
- # @return [Array<Sprachenfolge>]
- one_to_many :sprachenfolgen, :class => :Sprachenfolge
- end
- # Informationen zu Klassenbezeichnung und weiteren Daten über die jeweiligen
- # Bildungsgänge. Verfügbar als Assoziationen für Schüler.
- #
- # Tabellenname: eigeneschule_fachklassen
- class Fachklasse < Sequel::Model(:eigeneschule_fachklassen)
- end
- # Versetzungstabelle für Fachklassen. D.h. Klassenbezeichnungen für vorhergehende
- # und nachfolgende Klassen.
- #
- # Tabellenname: versetzung
- class Versetzung < Sequel::Model(:versetzung)
- # @!method fachklasse
- # @return [Fachklasse]
- many_to_one :fachklasse, :class => :Fachklasse
- end
- # Assoziation für Lehrer, hauptsächlich für Klassenlehrer
- #
- # Tabellenname: k_lehrer
- class Klassenlehrer < Sequel::Model(:k_lehrer)
- end
- # Ist die Assoziation, die Halbjahre, sog. Abschnitte zurückgibt.
- #
- # Tabellenname: schuelerlernabschnittsdaten
- class Abschnitt < Sequel::Model(:schuelerlernabschnittsdaten)
- # @!method noten
- # @return [Array<Note>]
- one_to_many :noten, :class => :Note
- # @!method fachklasse
- # @return [Fachklasse]
- many_to_one :fachklasse, :class => :Fachklasse
- # @!method klassenlehrer
- # @return [Klassenlehrer]
- many_to_one :klassenlehrer, :primary_key=>:Kuerzel, :key=>:KlassenLehrer, :class=>:Klassenlehrer
- end
- # Assoziation für Noten
- #
- # Tabellenname: schuelerleistungsdaten
- class Note < Sequel::Model(:schuelerleistungsdaten)
- # @!method fach
- # @return [Fach]
- many_to_one :fach, :class => :Fach
- end
- # Assoziation für Fächer
- #
- # Tabellenname: eigeneschule_faecher
- class Fach < Sequel::Model(:eigeneschule_faecher)
- # @!method sprachenfolge
- # @return [Sprachenfolge]
- one_to_one :sprachenfolge, :class => :Sprachenfolge, :key => :Fach_ID
- # @!method gliederungen
- # @return [Array<Fach_Gliederung>]
- one_to_many :gliederungen, :class => :Fach_Gliederung, :key => :Fach_ID
- end
- # Assoziation für BK-Abschluss des Schülers
- #
- # Tabellenname: schuelerbkabschluss
- class BKAbschluss < Sequel::Model(:schuelerbkabschluss)
- end
- # Assoziation für die Prüfungsfächer des Schülers
- #
- # Tabellenname: schuelerbkfaecher
- class BKAbschlussFach < Sequel::Model(:schuelerbkfaecher)
- # @!method fach
- # @return [Fach]
- many_to_one :fach, :class => :Fach
- end
- # Assoziation für Abi-Abschluss des Schülers
- #
- # Tabellenname: schuelerabitur
- class AbiAbschluss < Sequel::Model(:schuelerabitur)
- end
- # Assoziation für die Abifächer des Schülers
- #
- # Tabellenname: schuelerabifaecher
- class AbiAbschlussFach < Sequel::Model(:schuelerabifaecher)
- # @!method fach
- # @return [Fach]
- many_to_one :fach, :class => :Fach
- end
- # Assoziation für FHR-Abschluss des Schülers
- #
- # Tabellenname: schuelerfhr
- class FHRAbschluss < Sequel::Model(:schuelerfhr)
- end
- # Assoziation für die FHR-fächer des Schülers
- #
- # Tabellenname: schuelerfhrfaecher
- class FHRAbschlussFach < Sequel::Model(:schuelerfhrfaecher)
- # @!method fach
- # @return [Fach]
- many_to_one :fach, :class => :Fach
- end
- # Assoziation für die bisher erreichten Sprachniveaus
- #
- # Tabellenname: schuelersprachenfolge
- class Sprachenfolge < Sequel::Model(:schuelersprachenfolge)
- end
- # Besondere Facheinstellungen nach Fachklasse. Betrifft v.a. Sortierung,
- # Festlegungen über Prüfungsfächer etc.
- #
- # Tabellenname: fach_gliederungen
- class Fach_Gliederung < Sequel::Model(:fach_gliederungen)
- # @!method fachklasse
- # @return [Fachklasse]
- many_to_one :fachklasse, :class => :Fachklasse
- end
- # Vermerke von Schülern
- #
- # Tabellenname: schuelervermerke
- class Vermerk < Sequel::Model(:schuelervermerke)
- end
- # Schülerfotos als jpg
- #
- # Tabellenname: schuelerfotos
- class Schuelerfoto < Sequel::Model(:schuelerfotos)
- end
- # Schul-Tabelle
- #
- # Tabellenname: eigeneschule
- class Schule < Sequel::Model(:eigeneschule)
- end
- # Tabelle für Schild-Nutzer
- #
- # Tabellenname: users
- class Nutzer < Sequel::Model(:users)
- end
- end
- # ==SchildErweitert
- # Dieses Modul bindet das Schild-Modul ein und erweitert es mit zusätzlichen Methoden
- # zum einfachen Zugriff auf Schild-Methoden, die teilweise verschachtelt sind.
- # Ebenso bietet SchildErweitert alle Methoden mit Null-Objekten, d.h. es
- # gibt kein +nil+, nur Standardwerte.
- module SchildErweitert
- if Module.private_method_defined? :include
- # erst Ruby 2.1.0 macht include zu einer public-Methode
- class Module
- public :include
- end
- end
- # String und Symbol werden um snake_case ergänzt, das die Schild-Tabellen umbenennt
- # Legacy-Methoden aus alten Schild-Versionen wird teilweise auch unterstützt.
- module CoreExtensions
- # Patch für String
- module String
- # wandelt Strings in +Snake Case+ um.
- # Beispiel:
- # CamelCase.snake_case
- # => camel_case
- def snake_case
- return downcase if match(/\A[A-Z]+\z/)
- gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
- gsub(/([a-z])([A-Z])/, '\1_\2').
- downcase
- end
- end
- # Patch für Symbol
- module Symbol
- # snake_case für Symbol, verwandel Symbol in String
- def snake_case
- to_s.snake_case
- end
- end
- end
- # Schild hat teilweise nil in DB-Feldern. SchildTypeSaver gibt entweder einen
- # Leer-String zurück ("") oder bei strftime das 1899 Datum zurück.
- module SchildTypeSaver
- Symbol.include SchildErweitert::CoreExtensions::Symbol
- String.include CoreExtensions::String
- # es wird für jede Spalte in der Schild-Tabelle eine Ersatzmethode
- # erstellt, die bei nil ein Null-Objekt erstellt.
- # Dazu wird die neu angelegte Methode per MethodLogger gesichert.
- def self.included(klass)
- klass.columns.each do |column|
- name = column.snake_case
- MethodLogger::Methods.add(klass, name)
- # allow_nil ist als Argument optional und lässt bei +true+ alle Ergebnisse durch
- define_method(("_"+name.to_s).to_sym) {public_send(column)}
- define_method(name) do |allow_nil=false|
- ret = public_send(column)
- if allow_nil || ret
- ret = ret.strip if ret.class == String
- ret
- else
- create_null_object(klass, column)
- end
- end
- end
- end
- # Es wird ein Null-Objekt erstellt, das einen Standardwert zurückgibt.
- def create_null_object(klass, column)
- k = Sequel::Database::SCHEMA_TYPE_CLASSES[klass.db_schema[column][:type]]
- case
- # Sequel stellt :datetime als [Time, DateTime] dar, deswegen die Abfrage nach Array
- # Schild verwendet Time Objekte, wir machen das auch
- when k.class == Array then Time.new(1899)
- when k == Integer then 0
- when k == Float then 0.0
- # alle anderen types werden als Klasse zurückgegeben
- else k.new
- end
- end
- end
- # Halten wir Protokoll zu den erstellten Methoden
- # Ist brauchbar, wenn man z.B. noch extremer als der SchildTypeSaver arbeiten möchte
- module MethodLogger
- # Methoden-Klasse die alles Daten sammelt
- class Methods
- @@accessor_methods = {}
- # Eine Methode dem MethodLogger hinzufügen
- # @param [Class, method]
- def self.add(klass, meth)
- @@accessor_methods[klass] ||= []
- @@accessor_methods[klass] << meth
- end
- # Liste von Methoden für eine Klasse auslesen
- def self.list(klass=nil)
- klass ? @@accessor_methods[klass] : @@accessor_methods
- end
- end
- end
- # Mixin für Notenbezeichnungen
- module NotenHelfer
- # Noten können als Punkte abgerufen werden:
- # note[5] => "4-"
- # oder auch andersherum: note.index("4-") => 5
- @note = %w[6 5- 5 5+ 4- 4 4+ 3- 3 3+ 2- 2 2+ 1- 1 1+]
- # @param note [Integer]
- def self.punkte_aus_note(note)
- return if note.nil?
- @note.index(note)
- end
- # @param punkte [Integer]
- def self.note_aus_punkten(punkte)
- return unless punkte && punkte.to_i.between?(1,15) || punkte == "0"
- return punkte if ((punkte.to_i == 0) && (punkte.size > 1))
- return if (punkte.class == String) && punkte.empty?
- @note[punkte.to_i]
- end
- # Notenbezeichnung als String
- # @param ziffer [String]
- def note_s(ziffer)
- case ziffer
- when "1", "1+", "1-" then "sehr gut"
- when "2", "2+", "2-" then "gut"
- when "3", "3+", "3-" then "befriedigend"
- when "4", "4+", "4-" then "ausreichend"
- when "5", "5+", "5-" then "mangelhaft"
- when "6" then "ungenügend"
- when 'NB' then "––––––"
- when "E1" then "mit besonderem Erfolg teilgenommen"
- when "E2" then "mit Erfolg teilgenommen"
- when 'E3' then "teilgenommen"
- end
- end
- end
- # Klassen sind Konstanten. Deswegen alle auslesen, die Klassen behalten und
- # dynamisch neue Klassen mit gleichem Namen erstellen.
- # Automatisch SchildTypeSaver einbinden.
- #
- # Sollen zusätzliche Methoden eingebunden werden, muss - wie unten Schueler
- # und andere Klassen - die neu erstelle Klasse gepatcht werden.
- # Die alten Methoden bleiben erhalten, d.h. auch die TypeSaver-Methoden.
- Schild.constants.map {|name| Schild.const_get(name)}.select {|o| o.is_a?(Class)}.each do |klass|
- name = Schild.const_get(klass.to_s).name.split("::").last
- klass = Class.new(klass){include SchildTypeSaver}
- name = const_set(name, klass)
- end
- # Stellt die Schüler-Tabelle samt Assoziationen bereit.
- class Schueler
- # gibt das z.Zt. aktuelle Halbjahr zurück.
- def akt_halbjahr
- abschnitte.last
- end
- # gibt aus +jahr+ das Halbjahr +1+ oder +2+ zurück.
- def halbjahr(jahr, abschnitt)
- abschnitte_dataset.where(:jahr => jahr, :abschnitt => abschnitt).first
- end
- # gibt +Herr+ oder +Frau+ als Anrede für Schüler zurück.
- def anrede
- self.geschlecht == 3 ? "Herr" : "Frau"
- end
- # gibt die passende Bezeichnung zurück Schüler
- def schueler_in
- self.geschlecht == 3 ? "Schüler" : "Schülerin"
- end
- # gibt die passende Bezeichnung zurück Studierende
- def studierende_r
- self.geschlecht == 3 ? "Studierender" : "Studierende"
- end
- # gibt die jeweilige Berufsbezeichnung nach Geschlecht zurück.
- def berufsbezeichnung_mw
- return "Keine Fachklasse zugeordnet" if self.fachklasse.nil?
- self.geschlecht == 3 ? self.fachklasse.bezeichnung : self.fachklasse.beschreibung_w
- end
- # gibt +true+ zurück, wenn Schüler volljährig.
- def volljaehrig?
- self.volljaehrig == "+"
- end
- # gibt an, ob der Schüler zu einem Zeitpunkt *datum* volljährig war.
- def volljaehrig_bei?(datum)
- return false if datum.nil? || self.Geburtsdatum.nil?
- geb, datum = self.Geburtsdatum.to_date, datum.to_date
- (datum.year - geb.year - ((datum.month > geb.month || (datum.month == geb.month && datum.day >= geb.day)) ? 0 : 1)) >= 18
- end
- # fragt ab, ob in Schild ein Foto als hinterlegt eingetragen ist.
- def foto_vorhanden?
- !!(self.schuelerfoto && self.schuelerfoto.foto)
- end
- # gibt, wenn vorhanden, ein Foto als jpg-String zurück, ansonsten nil.
- def foto
- self.schuelerfoto.foto if self.foto_vorhanden?
- end
- end
- # Ist die Assoziation, die Halbjahre, sog. Abschnitte zurückgibt.
- class Abschnitt
- dataset_module do
- # filtert den Datensatz nach Jahr
- def jahr(i)
- where(:Jahr => i)
- end
- # filtert den Datensatz nach Halbjahr
- def halbjahr(i,j)
- jahr(i).where(:Abschnitt => j)
- end
- # filtert und gibt den Datensatz als Abschnitt des aktuellen Halbjahrs zurück
- def akt_halbjahr
- halbjahr(Time.new.year-1, 1).first
- end
- end
- # Hilfsmethode für die folgenden Methoden
- def faecher_nach_id(id)
- noten.select{ |n| n.fach.Fachgruppe_ID == id && n.AufZeugnis == '+' }.sort_by{ |n| n.fach.SortierungS2 }
- end
- # wählt alle berufsübergreifenden Fächer des gewählten Schülers in angegeben Halbjahr.
- def berufsuebergreifend
- faecher_nach_id 10
- end
- # wählt alle berufsbezogenen Fächer des gewählten Schülers in angegeben Halbjahr.
- def berufsbezogen
- faecher_nach_id 20
- end
- # wählt alle Fächer des Differenzierungsbreichs des gewählten Schülers in angegeben Halbjahr.
- def differenzierungsbereich
- faecher_nach_id 30
- end
- # wählt alle Fächergruppen aus.
- def faechergruppen
- [berufsuebergreifend, berufsbezogen, differenzierungsbereich]
- end
- # gibt den Namen des Klassenlehrers mit gekürztem Vornamen.
- def v_name_klassenlehrer
- return "Kein Klassenlehrer angelegt" if klassenlehrer.nil?
- v = klassenlehrer.vorname
- n = klassenlehrer.nachname
- "#{v[0]}. #{n}"
- end
- # gibt "Klassenlehrer" entsprechend Geschlecht zurück
- def klassenlehrer_in
- return "Kein Klassenlehrer angelegt" if klassenlehrer.nil?
- klassenlehrer.geschlecht == "3" ? "Klassenlehrer" : "Klassenlehrerin"
- end
- # gibt das aktuelle Schuljahr als String im Format "2014/15" zurück.
- def schuljahr
- jahr = self.jahr
- "#{jahr}/#{jahr-1999}"
- end
- end
- # Assoziation für Noten
- class Note
- include NotenHelfer
- # note in String umwandeln
- def note
- note_s self.noten_krz
- end
- # Bezeichnung des Fachs
- def bezeichnung
- fach.bezeichnung
- end
- # Die Fachgruppen-ID des Fachs
- def fachgruppe_ID
- fach.fachgruppe_id
- end
- end
- # Assoziation für BK-Abschlussdaten
- class BKAbschluss
- # Ist der Schüler zugelassen?
- def zulassung?
- self.Zulassung == "+"
- end
- # Ist der Schüler für den Berufsabschluss zugelassen?
- def zulassung_ba?
- self.ZulassungBA == "+"
- end
- # Hat der Schüler den Berufsabschluss bestanden?
- def bestanden_ba?
- self.BestandenBA == "+"
- end
- end
- # Assoziation für die jeweiligen BK-Prüfungsfächer
- class BKAbschlussFach
- include NotenHelfer
- # Wurde das Fach schriftlich geprüft?
- def fach_schriftlich?
- self.FachSchriftlich == "+"
- end
- # Wurde das Fach mündlich geprüft?
- def fach_muendlich?
- self.MdlPruefung == "+"
- end
- # holt die jeweilige Note aus der Tabelle.
- # Standard ist note_abschluss_ba, als Argument kann auch eine andere
- # verwendet werden (siehe Tabelle)
- def note(notenart=:note_abschluss_ba)
- note_s send(notenart)
- end
- end
- # Assoziation für Abi-Abschlussdaten
- class AbiAbschluss
- # Ist der Schüler zugelassen?
- def zulassung?
- self.Zugelassen == "+"
- end
- alias_method :zugelassen?, :zulassung?
- # Hat der Schüler die Abi-Prüfung bestanden?
- def bestanden_abi?
- self.PruefungBestanden == "+"
- end
- alias_method :pruefung_bestanden?, :bestanden_abi?
- # Latinum gemacht?
- def latinum?
- self.Latinum == "+"
- end
- # Kleines Latinum erreicht?
- def kl_latinum?
- self.KlLatinum == "+"
- end
- # Gräcum erreicht?
- def graecum?
- self.Graecum == "+"
- end
- # Hebraicum erreicht?
- def hebraicum?
- self.Hebraicum == "+"
- end
- end
- # Assoziation für die jeweiligen Abi-Prüfungsfächer
- class AbiAbschlussFach
- include NotenHelfer
- # Note aus Tebelle abfragen, Notenart angeben (siehe Tabelle)
- def note(notenart)
- note_s send(notenart)
- end
- end
- # Assoziation für die jeweiligen FHR-Prüfungsfächer
- class FHRAbschlussFach
- include NotenHelfer
- # Note aus Tebelle abfragen, Notenart angeben (siehe Tabelle)
- def note(notenart)
- note_s send(notenart)
- end
- end
- # Schul-Tabelle mit vereinfachtem Zugriff auf Datenfelder mittel class-Methoden
- class Schule
- # gibt die Schulnummer zurück
- def self.schulnummer
- self.first.schul_nr
- end
- # gibt den Namen des Schulleiters als V. Name zurück
- def self.v_name_schulleiter
- "#{self.first.schulleiter_vorname[0]}. #{self.first.schulleiter_name}"
- end
- # gibt die männliche bzw. weibliche Form des Schulleiters zurück
- def self.schulleiter_in
- self.first.schulleiter_geschlecht == 3 ? "Schulleiter" : "Schulleiterin"
- end
- # gibt den Ort der Schule zurück
- def self.ort
- self.first.ort
- end
- end
- # Tabelle der Schuld-Benutzer zum Abgleichen der Daten
- class Nutzer
- alias :name :us_name
- alias :login :us_login_name
- alias :passwort :us_password
- alias :password :passwort
- # prüft, ob das angegebene Passwort mit dem gespeicherten Passwort übereinstimmt
- def passwort?(passwort='')
- crypt(passwort) == self.passwort
- end
- alias :password? :passwort?
- # ver- bzw. entschlüsselt einen String mit dem Schild-Passwortalgorithmus
- def crypt(passwort)
- passwort.codepoints.map{|c| ((c/16)*32+15-c).chr}.join('')
- end
- end
- end
|