Irgendwann fängt man an aus seinem großen Framework einzelne kleine Module heraus lösen zu wollen. Die will man dann als Composer-Requirements wieder einbinden. Zum Glück geht das sehr einfach, indem man einfach ein Repository zu seiner composer.json hinzufügt:
Manchmal will man später Dateien oder Verzeichnisse in die .gitignore aufnehmen, die man vorher nicht drin hatte und mit versioniert hatte. Das Hinzufügen zur gitignore reicht aber nicht, um die Dateien aus dem Repository verschwinden zu lassen.
Da muss man zuerst
git rm -r --cached .
ausführen und dann wieder alle Dateien adden. Dann werden die nun ignorierten Dateien als gelöscht in Git geführt.
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