Was man in Java teilweise mit Javassist gemacht hat und man für die Erkennung von Änderungen an Object-Values in JavaScript dringend braucht sind Proxy-Objekte.
Das geht zum Glück an sich ganz einfach.
<html>
<head>
<script type="text/javascript">
var x = {value: ""};
Ein sehr guter Wired-Artikel Auch 2029 wird es keine Künstliche Intelligenz geben, die diesen Namen verdient, der endlich mal klarstellt, dass das was man seit kurzen überall als KI verkauft, nichts weiteres ist als klassische Mustererkennung ist, wie man sie seit Anfang der 90er nutzt. Der klassische KI-Begriff, in dem um die Schaffung einer Intelligenz geht, hat damit garnichts zu tun.
Mich stört es wenn Firmen von KI sprechen und man dahinter nur einfache Datenanalyse mit stupiden Bedingungen und Gewichtungen erkennt. Vektoren zum Vergleichen mit bekannten Mustern sind jetzt auch nicht wirklich neu oder irgendwie intelligent.
Am Ende eines Sprints soll etwas heraus kommen, was man dem Kunden zeigen kann bzw deliverable ist. Kunden an sich können nur sehen was klickbar ist. Dinge die nicht klickbar sind sind für die meisten Kunden einfach nicht existent, weil diese zu meist keine technischen Anforderungen stellen sondern fachliche. Man Klickt etwas an und es passiert was und das ist das was der Kunde sieht und bewertet.
Aber muss es so sein? Es gibt viele Dinge innerhalb eines Sprints, die die Entwicklung und technische Basis voran bringen ohne das ein Anwender sie bemerkt oder sehen kann. Deswegen ist es vielleicht falsch nur den Kunden die Bewertung zu überlassen. Der Kunde muss nur am Ende etwas sehen, das man messen und dann validieren kann.
Angenommen, ich möchte mehr Automatisierung in der UI haben. Formulare sollen sich automatisch erzeugen und nicht mehr per Hand zusammen gebaut werden müssen. Kann ich das in einem Sprint nach der Definition liefern? Ja! Ich baue es ein und liefere dem Kunden am Ende des Sprints einen Mess-Fall. Genau so ob die Änderung die der Kunde sieht er nach dem Spring validiert werden kann, ob der erhoffte Impact eintritt, kann meine technische Änderung genau so zur selben Zeit getestet werden. Zwar nicht durch einen Anwender sondern durch die Daten des nächsten Sprints selbst, aber das Ergebnis ist das Selbe.
Ich liefere etwas und gebe dem Kunden etwas an die Hand, um die Hypothese auf der dieses gelieferte fußt, prüfen zu können. Ich sage, ich kann mit dieser Änderung im nächsten Sprint 6 neue Formulare in der UI liefern und nicht mehr nur 2. Das ist eine gute Annahme, die sich überprüfen lässt. Ob eine andere Userstory, die vom Kunden selbst kommt, am Ende das erhoffte liefert, weis man genau so wenig, wie diese Annahme, mit der ich den Form-Generator eingebaut habe.
Nach dem folgenden Sprint, gehe ich wieder zum Kunden und zeige ihm, was wir an Formularen gebaut haben. Nun prüft er, ob meine Annahme mit 6 Formularen erfüllt wurde. Vielleicht sind es nur 5 geworden oder wir hatten sogar Zeit über, in der wir noch ein Formular mehr hätten machen können... das ist gar nicht ganz so wichtig, weil sich heraus gestellt hat, dass es eine Verbesserung gab und damit meine technische Userstory erfüllt werden konnte und nicht nochmal neu bearbeitet werden oder als Fehlschlag einzustufen ist.
Ob man also klickt oder am Ende Zahlen vorlegt, ist für den Kunden dann egal. Man hat jeweils etwas gebaut, was sich in der Zeit danach bewähren muss und nur das was dann da heraus kommt zählt und nicht was am Ende des Sprint sichtbar war. Deswegen muss ich am ende des Sprints immer nur etwas liefern, was messbar ist. Nicht etwas was direkt sichtbar ist.
Wenn man mit JavaScript zu tun bekommt, führt leider kein Weg an Selenium vorbei. Das erst einmal zum laufen zu bekommen ist dabei auch nicht immer einfach. Ich habe dabei gelernt, dass Firefox nicht wirklich funktioniert und Chrome dagegen ohne Probleme funktioniert. Um Selenium zum Laufen zu bekommen brauchen wir einiges:
- eine aktuelle Version des Selenium Standalone Servers als JAR
- den Chrome-Driver für Selenium
- Java
dann kann man Selenium mit java -Dwebdriver.chrome.driver=/opt/selenium/chromedriver -jar selenium-server-standalone-X.X.X.jar starten. Den Driver kann man natürlich auch in anderen Verzeichnissen ablegen.
https://selenium-release.storage.googleapis.com/index.html
Um nun auch eigene Methoden wie Warten hinzuzufügen brauchen wir einen eigenen Context. Den legen wir unter features/bootstrap ab.
<?php
class TestContext extends \Behat\MinkExtension\Context\MinkContext {
/**
* @When I wait :arg1 seconds
*/
public function iWaitSeconds($seconds)
{
$this->getSession()->wait($seconds * 1000);
}
/**
* @When I click the :arg1 element accepting the alert
*/
public function iClickTheElementAcceptingTheAlert($selector)
{
$page = $this->getSession()->getPage();
$element = $page->find('css', $selector);
if (empty($element)) {
throw new Exception("No html element found for the selector ('$selector')");
}
Nun können wir Warten und sogar Javascript Dialoge bestätigen.
Wenn wir nun mit "bin/behat features/XXX.feature" unser Test Scenario XXX starten öffnet sich der Chrome-Browser und wir können alle Tests visuell mit verfolgen.
Leider kann man über den Orange Pi One nicht über den USB-Anschluss mit Strom versorgen sondern braucht ein 5V 2A Netzteil. Ich habe mir dieses Aukru 8 in 1 USB Ladegerät gekauft und mit den Adaptern passte es dann auch und der Adapter sitzt auch so, dass ich erst einmal den Karton als vorläufiges Gehäuse verwenden kann.
Damit stehen den Experimenten mit Motioneye OS nichts mehr im Weg. Wenn das nichts wird, kommt einfach RetroPi rauf :-)
Wer seinem PC oder NAS zu Weihnachten eine 2TB HDD spendieren möchte bekommt bei notebookswieneu.de gerade sehr günstig für 30 EUR refurbished (mit etwas Glück auch mal eine neue dazwischen) welche. Ich hab ein paar schon mal welche (auch für Kunden) verbaut und bis jetzt lief alles immer super und alle Werte waren super.
Eigene SEO-Urls kann man mit Shopware relativ einfach hinzufügen, wenn man die technischen Urls kennt. So kann man auch einem Blog-Post eine zweite SEO-Url anlegen, die nicht direkt als Blog-Url erkennbar ist. Diese Urls sind keine Weiterleitungen mit 301 oder 302 sondern echte Urls, die den selben Content liefern, wie die originalen Urls, was bei SEO zu beachten ist.
Das Mocking von Services war lange eine sehr komplizierte Sache. Mit Javassist oder speziellen Implementierungen konnte man es lösen, aber es war oft einfach nicht schnell und einfach zu benutzen. Mit Mockito kann man pro Test die Method-Calls einer Klasse überschreiben und so relativ einfach auch Test für Klassen mit Dependency Injection schreiben. Wenn man eine Service-Klasse testen möchte, die auf ein DAO zurückgreift um Daten aus der Datenbank zu laden, kann man nun einfach die DAO-Klasse mit Mockito so manipulieren, dass sie bestimmte Daten liefert ohne auf die Datenbank zu gehen und so hat man die volle Kontrolle über die Input- als auch die Output-Daten.
import static org.mockito.Mockito.*; //to use when() without static class
public class ModelTest {
//will always return a model with id = 0 (not existing), simulating an empty/not existing database
@Mock
private TestModelDAO dao;
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Before
public void setup(){
//creating an existing dummy for id 23, so 23 will always return a valid model
TestModel dummy = new TestModel();
dummy.setId(23);
dummy.setName("blubb-23");
when(this.dao.getModel(23)).thenReturn(dummy);
}
@Test
public void getModelSimple(){
TestModel model = (new TestModelDAO()).getModel(42);
Assert.assertEquals(0, model.getId());
}
@Test
public void getModelComplexId(){
TestModel model = this.dao.getModel(23);
Assert.assertEquals(23, model.getId());
}
@Test
public void getModelComplexName(){
TestModel model = this.dao.getModel(23);
Assert.assertEquals("blubb-23", model.getName());
}
@Test
public void getModelDIExample(){
//Testing a service with constructor dependency injection
TestService service = new TestService(this.dao);
String upperName = service.getUpperCaseName(23); //name for id 23
Assert.assertEquals("BLUBB-23", upperName);
}
}
An sich sollte man solche Methoden wie getUpperCaseName nie so schreiben und immer die fertig geladene Entität rein reichen. Aber gerade bei älteren Legacy-Code findet man solche Dinge oft. Auch Tests mit fehlenden Request oder ähnlichen kann man so durchführen, ohne direkt eine gesamte HTTP-Umgebung nachbauen zu müssen.
Irgendwie scheint das v-for von Vue.js Probleme zu haben, wenn ein Array z.B. von 5-10 geht und nicht von 0-5. Ich musste jeden Falls erst einmal einen Offset berechnen, um im Array wieder alles von 0 beginnen zu lassen. Dann funktionierte wieder alles.
Ich musste mich damit beschäftigen wie man eine kleine REST-API mit Python erstellt. Django macht dabei an sich bei jeden Pups bei mir Probleme und war doch sehr umständlich, weil man sich noch mit dem gesamten MVC-Pattern darin beschäftigen musste. Nachdem es auch mit dem ORM schwieriger wurde (im Vergleich zu Spring Boot mit JPA/Hibernate) dachte ich mir, es müsse doch auch für Python was modernes geben. So kam ich zu Turbo Gears 2 und das macht schon mal genau was ich wollte. Einfach, schnell und übersichtlich.
from tg import expose, TGController, AppConfig
import jsonpickle
# --
class TestEntity(object):
def __init__(self, id ,name, sub):
self.id = id
self.name = name
self.sub = sub
class TestSubEntity(object):
def __init__(self, value):
self.value = value
# --
class RootController(TGController):
@expose("json")
def index(self):
test = TestEntity(42, 'blubb', TestSubEntity('sub-blubb'))
return jsonpickle.encode(test, unpicklable=False)
print("Serving on port 8090...")
httpd = make_server('', 8090, application)
httpd.serve_forever()
Mit Turbo Gears 2, Spring Boot und Meecrowave kann wirklich schnell und einfach Microservices erstellen und vieles des Overheads alter Zeiten ist einfach nicht mehr da bzw wurde so gut versteckt, dass man sich rein auf den Code und die Logik konzentrieren kann. Welche Lösung man da nimmt ist Geschmackssache. Von der Codestruktur her sieht alles an sich fast 100% gleich aus.
Bei Python sehen Flask, Bottle und Hug auch interessant aus
Ich mag PHP. Ich mochte PHP schon zu PHP4- und PHP5-Zeiten, obwohl da Performance und Typisierung wirklich nicht immer so toll waren. Aber es gab einen großen Vorteil, im Vergleich zu Java (Tomcat + JSP/JSF/Servlets), den man konnte einfach eine Datei ändern und F5 drücken. Dann war die Änderung direkt zu sehen. Das Betraf auch nciht nur einfache Controller und Views sondern auch DB-Connections, Caches und andere Dinge bei denen man normaler Weise den Java Servlet-Controller hätte neustarten müssen. Dieser Luxus wurde sich eben dadurch erkauft, das es keinen Application-Scope oder komplexes Connection-Pooling in PHP gab. Aber dafür ersparte man sich nicht-funktionierende Hot-Deploys und Restart-Zeiten der Serverumgebung.
Nun soll es einen PHP Preload-Cache geben, wobei die Kompilierung des PHP-Codes nur noch beim Serverstart erfolgen soll: A server restart will be required to apply the changes. Damit werden dann einige (die ersten) Seitenaufrufe schneller, weil dann dort das Kompilieren entfällt. Bei Blogs oder one-page Lösungen sehe ich da keinen wirklichen Vorteil. Die Kompilierungszeiten mit PHP 7 sind wirklich schnell geworden, so das auch der erste Aufruf der 90% der Anwendung kompilieren würde bei dem normalen OpCache, gefühlt nur minimal langsamer wäre als die weiteren Aufrufe. Der absolute Nachteil wäre, das man den Server wegen jeder Änderung neustarten müsste. Also würde man sich Scripte schreiben, die das Starten und Kompilieren für einen übernehmen. Dann noch WebPack und man hat einen Workflow, der genau so komplex ist wie bei Java-Projekten (nur dass dort mit Maven alles zentraler geregelt wird).
Was habe ich also am Ende. Komplexität, Deployment-Zeiten, eine immer noch nicht ganz so überzeugende Performance, kein Multithreading, keinen Application-Scope und kein Connection-Pooling für Datenbankverbindungen. Also alles was an Java nicht so toll ist und keinen der Vorteile von Java.
Besonders, wenn man immer mehr in die Richtung geht einen HTML5+JS Client mit einer REST-API/Middleware anstelle klassischer MVC-Modelle zu nutzen, wären die F5-Vorteile von PHP wirklich toll gewesen.
Wer also sehr komplexe Seiten mit vielen vielen verschiedenen Views hat, der kann Vorteile durch einen Preload-Cache haben. Bei allem anderen sollte der OpCache allein noch die Performance bestimmen.
Unit-Tests sind ja immer ganz schön, um Berechnungen und andere atomare Logiken zu testen. Berechne ich den Preis richtig? Funktioniert das Regex- oder XPath-Pattern noch? Kann man alles mit Unit-Tests super und zu zuverlässig testen. Wenn es kann aber um Workflows oder Benutzerführung geht wird es schwer. Auch muss ein UX-Spezialist nach jeder neuen Version wieder testen, ob die von ihm festgelegten Wege und Regeln noch genau so funktionieren und der Benutzer auch das zusehen bekommt was er soll und nicht plötzlich in einer falschen Ansicht landet.
Hier kommt Mink ins Spiel. Mink wird verwendet von Behat und das ist ein Behavior Driven Development Framework. Wie auch bei Unit-Tests wird erst formuliert, was wo passieren soll und wie der Besucher sich auf der Seite bewegt und was er wann zu sehen bekommt.
Auf meinem Blog soll ein Besucher mit der Hauptansicht des Blogs starten und wenn er nach "rfid" sucht, den Post mit "POS-Plugin Cashless-Payment Demo" finden. Außerdem soll er beim PoE-Kameras Post Kommentare hinterlassen können.
Behat aufsetzen ist nicht immer ganz einfach, weil es beim Composer teilweise zu Versionsproblemen kommen kann. Deswegen hier mein kleines Grund-Setup.
und jetzt kommt der Test unter features/home.feature:
Feature: Home
Scenario: Starting with blog index page
Given I am on "/"
Then I should see "Latest Entries"
Scenario: Search for RFID
Given I am on "/index.php?page=Blogs&sub=search"
When I fill in "pattern" with "rfid"
And I press "search"
Then I should see "POS-Plugin Cashless-Payment Demo"
Scenario: Display comment-fields in blog-post
Given I am on "/idx-blog--berwachtungskameras-mit-poe-und-nas.html?page=Blogs&sub=viewBlog&blogId=465"
Then I should see "write comment:"
Scenario: Display comment-fields in blog-post 2
Given I am on "/idx-blog--berwachtungskameras-mit-poe-und-nas.html?page=Blogs&sub=viewBlog&blogId=465"
Then I should not see "Not able to write comment"
Und das war es auch schon. Jetzt kann man den Test über bin/behat features/home.feature starten und es wird alles einmal durch getestet.
Allein mit dem wenigen Syntax den ich dort verwendet habe kommt man schon relativ weit. Wenn man stark JavaScript-basierte WebApps testet gibt entsprechende Erweiterungen, um auch direkt selbst mit JavaScript dinge triggern oder Events abfeuern zu können.
Vor einiger Zeit hatte ich eine PLZ-Suche implementiert, bei der Locations bis zu einer bestimmten Distanz zu einer 2. per PLZ identifizierten Location als Ergebnismenge heraus gesucht wurden.
Es wurde also Also eine Distanz zwischen 2 per GPS-Koordinaten beschrieben Locations berechnet. Ich habe das in PHP erledigt, weil die Locations der zu findenen Orte in einer JSON-Datei vorlagen und ich mir den Import in die MySQL-DB ersparen wollte. Die Orte per PLZ kamen aber aus der MySQL-DB, wobei immer der erste Ort mit der PLZ die Koordinaten geliefert hat.
Auf der Seite findet man auch Code für PL/SQL (Oracle), Java und JavaScript. Nur leider für MySQL nicht. Die findet man aber hier. Im Grunde kann man sich das aber für jede Sprache selbst ableiten, weil es normale Mathematische Berechnungen sind ohne dass zusätzliche Libs oder Klassen nötig wären (die es aber auch gibt und diese Logik kapseln).
Jeden Falls sind solche Berechnungen an sich ganz einfach und performant, so dass man sich alles schnell selbst schreiben kann und nicht auf 3rd Party Lösungen angewiesen ist.