Yesterday, menasquez posted a comment asking me a lot of questions regarding Gumdrop and this blog.
I figured I should answer them in an article (as he has a lot of questions ^^) and make it a first draft of Gumdrop's FAQ.
"Do I need to learn PHP to use Gumdrop?"
No you don't. Really. However, if you want to build your own templates, you need to know some HTML, some CSS and some Twig. Check the documentation for template designers to learn more about Twig's syntax.
If you want to add some dynamic features to your site, you'll need to use JavaScript or some other client-side language as Gumdrop generates static web sites.
"Do you need to write a new post in html to add it on your blog or do you have an alternative way?"
No, the goal of Gumdrop is to convert Markdown to HTML. So my workflow to post a new article on this blog is quite simple:
r and w options of gumdrop: _vendor/bin/gumdrop -rw and go to http://localhost:8000/,"What are the files you modified to build your template?"
Well, Gumdrop is agnostic to the type of site you want to build with it. You can build a blog, you can build a technical documentation, etc. To build a blog with Gumdrop, some work is needed.
First, you need a template for your articles. Take a look at mine. It's a Twig file in the _layout folder because it's applied to the generated HTML files. It's included on the more global default.twig template. It displays information generated by Gumdrop: {{ page.title }}, {{ page.tags }}, {{ page.date }} (these are generated from the Markdown file's header), {{ content }} (this one contains the generated HTML content).
Then you need a page listing the articles. Mine is there. It also extends the default layout. Whatever you put between {% block content %} and {% endblock %} will replace {% block content %}{% endblock %} in default.twig. It iterates (loops) the articles passed by Gumdrop in the pages array. This iteration mechanism can be used also for your web site's map and RSS feed.
This is basically the blog-specific stuff I did, the rest is explained in Gumdrop's documentation.
"How did I implemented the search engine on my blog?"
I used the iteration mechanism I just talked about to build a JSON representation of all the articles on my blog. Then with some JavaScript, I query this JSON file, filter the articles that match the search string then display the list.
"How you installed it on the server? What are the requirement on the server side?"
All you need on your machine is a web server able to serve static files. The usual suspects are Apache, Nginx, Lighttpd, IIS, etc. I personally use Nginx.
My publishing workflow involves Git. My blog's main Git repository is hosted on the same machine that serves my blog. When I push my changes to the server, a Git hook automatically generates the blog using Gumdrop.
Here it is:
echo "Updating Github repository" git push github master echo "Updating sources" cd /var/www/blog/ env -i git pull origin master echo "Updating Composer" env -i /usr/local/bin/composer self-update echo "Updating dependencies" env -i /usr/local/bin/composer update echo "Generating the blog" _vendor/bin/gumdrop
Basically, it updates the blog's Github mirror, updates the sources in the generation folder, updates Composer, updates Gumdrop and finally generates the blog.
If any of you have more questions about Gumdrop or this blog, please post a comment here.
As promised on Twitter, I'm going to explain how I'm now using Vagrant to develop Gitrepos instead of running it on my development Macbook Air.
Against my better judgment, I started developing Gitrepos on my Mac without bothering with creating an Ubuntu VM running on VMWare Fusion as I did for other projects in the past. At the time, I figured that if I used the correct libraries and abstracting my code enough, I could develop with PHP built-in server and SQLite. I already wrote here that I don't like running servers on my Mac for 2 reasons:
While I was working on Gitrepos, I had this nagging feeling that I should start testing it on a production-like environment ASAP. I was sure I was building technical debt and, guess what, I was...
In this commit (github.com/simonjodet/git…) I've switched my dev environment to a vagrant VM. PHP server -> Apache, SQLite -> MySQL...
— Simon Jodet (@sjodet) February 8, 2013
...I found out Apache changes exotic HTTP codes such as 230 into 500 and that "keys" is a reserved word for MySQL but not for SQLite.
— Simon Jodet (@sjodet) February 8, 2013
Then the other day, I went through my (very long) Pocket list and took a look at Vagrant. I mainly use Pocket as note pad/todo list and I had flagged Vagrant as something I should take a look at. It looked exactly as the best solution to solve my environment problem.
But I used VirtualBox in the past and I remember it being a PITA to setup, especially the folder sharing stuff. So I gave Vagrant a try, almost convinced it would failed. But it didn't!
I'm amazed how little hassle it is to use Vagrant: in 2 hours, I've created a base box from scratch and setup Chef to create a LAMP server
— Simon Jodet (@sjodet) February 7, 2013
I eventually dumped Chef to use a basic shell script though:
#!/bin/sh
echo mysql-server mysql-server/root_password select "vagrant" | debconf-set-selections
echo mysql-server mysql-server/root_password_again select "vagrant" | debconf-set-selections
apt-get install -y mysql-server apache2 php5 libapache2-mod-php5 php5-mysql
VHOST=$(cat <<EOF
<VirtualHost *:80>
DocumentRoot "/vagrant/web"
ServerName localhost
<Directory "/vagrant/web">
AllowOverride All
</Directory>
</VirtualHost>
EOF
)
echo "${VHOST}" > /etc/apache2/sites-enabled/000-default
sudo a2enmod rewrite
service apache2 restart
mysql -u root -p"vagrant" -e ";DROP DATABASE test;DROP USER ''@'localhost';CREATE DATABASE gitrepos;GRANT ALL ON gitrepos.* TO gitrepos@localhost IDENTIFIED BY 'gitrepos';GRANT ALL ON gitrepos.* TO gitrepos@'%' IDENTIFIED BY 'gitrepos'"
sed -i 's/127.0.0.1/0.0.0.0/g' /etc/mysql/my.cnf
service mysql restart
apt-get clean
Going through this script quickly:
apt-get runs silently and set the correct passwordsAnd here's my Vagrantfile:
# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant::Config.run do |config| config.vm.box = "ubuntu_server_12_10_amd64" config.vm.network :hostonly, "192.168.242.2" config.vm.provision :shell, :path => "deploy/deploy.sh" end
As you may have noticed I don't use a NAT network for my VM but a "Host-Only" network so I have a fixed IP that is only accessible from the host it's running on. That way I don't expose my application to the outer world.
But I have 2 issues with Vagrant:
I'm not a paranoiac but since I worked for a security company, I smell a security issue here. Gitrepos' code is public but I wouldn't trust a base box built by an unknown 3rd-party for private code. It's way too easy to make one that would leak code.
So I decided to build my own Ubuntu Server 12.10 amd64 base box. Read more...
First, you'll need to install Virtualbox and download the Ubuntu Server image.
In a few screenshots, here's how to start the setup:










The first step of the Ubuntu installer are pretty straight forward and entirely up to you.
Then you get asked for the Hostname. Vagrant's documentation suggests to follow a convention: vagrant-[os-name], e.g. vagrant-ubuntu-12-10-amd64 in our case.
Then for the next steps:
vagrantvagrantvagrantI don't encrypt the disk because we don't care in this setup. For partitioning, I use the simplest option, skipping LVM because we want to avoid unnecessary stuff that could impair performance and make the VM larger than necessary.
Let it then install stuff. I don't enable automatic updates, it could mess up some test runs, just remember to apt-get update; apt-get upgrade once in a while. At the "software selection" step, don't check anything, we'll take care of that later.
Finally, install Grub with the default options and reboot.
At this step, since the VM's network uses NAT, you can't login through SSH and copy-pasting to the VM is not possible.
Login as vagrant/vagrant. I've aggregated the different steps in a bash script. Just follow these steps:
cd /tmp wget http://blog.jodet.com/uploads/vagrant.sh chmod +x vagrant.sh sudo ./vagrant.sh
The last step is to let the vagrant user "sudo" without having to type his password.
sudo chmod 644 /etc/sudoers sudo vim /etc/sudoers
Add Defaults env_keep="SSH_AUTH_SOCK" to the "Defaults" and replace %admin ALL=(ALL) ALL by %admin ALL=NOPASSWD: ALL.
It should look like this:
# # This file MUST be edited with the 'visudo' command as root. # # Please consider adding local content in /etc/sudoers.d/ instead of # directly modifying this file. # # See the man page for details on how to write a sudoers file. # Defaults env_reset Defaults mail_badpass Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" Defaults env_keep="SSH_AUTH_SOCK" # Host alias specification # User alias specification # Cmnd alias specification # User privilege specification root ALL=(ALL:ALL) ALL # Members of the admin group may gain root privileges %admin ALL=NOPASSWD: ALL # Allow members of group sudo to execute any command %sudo ALL=(ALL:ALL) ALL # See sudoers(5) for more information on "#include" directives: #includedir /etc/sudoers.d
Now you're done, just shutdown the system with sudo shutdown -h now.
Open a terminal, go to the folder containing your VM and run:
cd VirtualBox\ VMs/ubuntu_base vagrant package --base ubuntu_base
Your new base box is named package.box. You can now add it to your system and use it in a project like this:
vagrant box add vagrant-ubuntu-12-10-amd64 package.box mkdir test_environment cd test_environment vagrant init vagrant-ubuntu-12-10-amd64 vagrant up vagrant ssh
You can store the package.box file in a safe location and delete the VirtualBox VM if you need space.
Sources: Creating a vagrant base box for ubuntu 12.04 32bit server and the official documentation.
I've learned a long time ago that PHPUnit mocks suck. Big time. They're just a pain to use.
So I looked for alternatives. I didn't look for long as a former colleague of mine is one of the original contributors of atoum.
Maybe because it's younger (and thus requires PHP 5.3 or above), atoum expressiveness is much better than PHPUnit.
I really like atoum and highly recommend it. However, because it's a young project, I have two problems with it:
The integration issue for me is really a deal-breaker. I really like to have a green bar in IntelliJ IDEA. I sometimes like to check test coverage live in the IDE. There was no Jenkins plugin for atoum (I didn't check lately though).
Update: atoum works with Jenkins as his author indicates to me. And I should have mentioned the feature request for atoum support in IntelliJ IDEA. I signed it and you should too.
And then I stumbled on Mockery. Unlike atoum, it doesn't replace PHPUnit, it just replaces its mocking system with a more elegant, powerful and reliable one.
The big plus of Mockery over PHPUnit is that you don't need to have the class to mock to create a mock. You can start mocking a dependency, test and write your class then later create the dependency class. Oh and try mocking Demeter chains with PHPUnit. Good luck!
But writing partial mocks with Mockery is not obvious. "What is a partial mock?" you say? It's mocking some method(s) of a class to test other methods of the same class.
Here's a real life example. Let's say we have a Database class with:
dropTable method to drop a given tablelistTables method to list existing tables in a given databasegetSchema method to get the database SQL schemaThose 3 methods will be used to reset (empty it of all data) the database: list tables, delete them then execute the SQL requests of the schema.
To me, there is no other logical place for a reset method than the Database class. That's a typical scenario when you need a partial mock.
Here's the Database class:
<?php
class Database
{
/**
* @var \Doctrine\DBAL\Connection
*/
private $conn;
public function __construct(\Silex\Application $app)
{
$this->conn = $app['db'];
}
public function getSchema($version = null)
{
//...
}
public function listTables()
{
//...
}
public function dropTable($table)
{
//...
}
public function reset($version = null)
{
$tables = $this->listTables();
foreach ($tables as $table)
{
$this->dropTable($table);
}
$schema = $this->getSchema($version);
foreach ($schema as $query)
{
$this->conn->query($query);
}
}
}
To create partial mocks, you have to give Mockery the class to mock (so don't create an anonymous mock) and the methods that will be mocked surrounded by square braces:
$Database = \Mockery::mock('MyClass[methodToMock1,methodToMock2]', array('constructor parameter 1', 'constructor parameter 2'))
You can also pass some parameters to the mock's constructor. It's especially handy when you need to inject external dependencies.
And here's how it's applied to our example:
<?php
namespace Tests\UnitTests;
class DatabaseTest extends PHPUnit_Framework_TestCase
{
//...
public function test_reset_lists_existing_tables_deletes_them_then_create_a_new_schema()
{
$app = new \Silex\Application();
$ConnectionMock = \Mockery::mock('\Doctrine\DBAL\Connection');
$ConnectionMock
->shouldReceive('query')
->with('SQL query 1')
->once()
->ordered('reset');
$ConnectionMock
->shouldReceive('query')
->with('SQL query 2')
->once()
->ordered('reset');
$app['db'] = $ConnectionMock;
$Database = \Mockery::mock('\Database[listTables,dropTable, getSchema]', array($app));
$Database
->shouldReceive('listTables')
->once()
->ordered('reset')
->andReturn(array('system', 'users'));
$Database
->shouldReceive('dropTable')
->with('system')
->once()
->ordered('reset');
$Database
->shouldReceive('dropTable')
->with('users')
->once()
->ordered('reset');
$Database
->shouldReceive('getSchema')
->with(1)
->once()
->ordered('reset')
->andReturn(array('SQL query 1', 'SQL query 2'));
$Database->reset(1);
}
}
In a matter of a week, I've been "trolled" twice - I may be exaggerating a bit here - on Twitter by two very serious and talented developers: @dhh (creator of Ruby on Rails) and @JBossMike (a core engineer at JBoss).
First, I got a bit pissed at @dhh for this tweet:
It's the same thing about PHP. Its simplicity and niche was and is amazing. Trying to grow it into a full OO env is folly.
— DHH (@dhh) December 29, 2012
I could have let it go but it was not the first time I saw a Ruby developer bashing PHP about it being simple or messy or not professional.
So I answered with:
And now @dhh is trolling about PHP… You should look at Symfony 2 or Zend Framework, you'll learn a couple of things about true OOP...
— Simon Jodet (@sjodet) December 29, 2012
and got his attention with:
.@dhh tweets PHP is not a full OO env just weeks after @gilesgoatboy publishes a book about how Rails gets OOP wrong: railsoopbook.com
— Simon Jodet (@sjodet) December 29, 2012
The discussion went on for a while but basically, @dhh said he admired PHP for its original simplicity but it shouldn't have evolved toward OOP.
Then today I had a Twitter conversation with @JBossMike who reacted to this tweet of mine:
ROFL "Java has eclipsed most dynamic languages" ocpsoft.org/opensource/jav… Except at Facebook, Twitter, Google, Dropbox and the list is long...
— Simon Jodet (@sjodet) January 5, 2013
Basically, he took offence of "ROFL" associated with Java - or maybe because I made fun of his colleague's article - and spent a surprising amount of time listing how much Facebook, Twitter and especially Google were using Java and how much Java is scalable. I can't argue with that, he probably knows better than I do what language is used at Google and it's absolutely true some very scalable tools like Cassandra or Hadoop are coded with Java.
But, like @dhh, @JBossMike missed my point or ignored it. There is a good reason Google was initially using Python. Facebook still uses PHP heavily and Twitter was created with Rails.
The reason is those languages and their ecosystem allow for a quick development and are easily deployable. I'm no Java expert but I'd suspect Java doesn't perform as well on small architectures as those 3 languages.
I'd also suspect that in order to deliver a working prototype of Facebook, Google or Twitter, the amount of code/work necessary in Java is higher.
And finally, I think Java developers are more expensive than their fellow developers specialised in PHP, Python or Ruby.
"Oh look what he's done in this last chapter, he's trolling Java!" Maybe but I just wanted to prove that Java, like Ruby (and Rails) are not the silver bullet that solves every problem.
I could go on about Java, even though I know very little about it and not be that far from the truth. I mean, the article that started the discussion with @JBossMike is referring CoffeeScript replacing JavaScript. Like Scala is replacing Java?
"Java is a strongly-typed language so it's more maintainable and failure-proof" I've worked several years with very talented C/C++ developers and they still had much more difficulties refactoring their apps than me with my MVC framework written in PHP. Strongly-typed has nothing to do with code quality or maintainability.
It happens I'm reading Clean Code again. All the examples are in Java but I can't remember an occurrence where Uncle Bob refers to Java being strongly-typed having anything to do with writing clean code.
You know why Cassandra or Hadoop are written in Java? Because they're developed by better developers!
Yes they're better developers, more experienced, more trained, more schooled. But they're not better because they use Java. Those guys just tend to have learned Java early in their career and feel comfortable using it.
But you know what? Two of the best developers I know are not Java developers (I mean Java is not their prefered language). Fabien Potencier is the creator of the Symfony project (among other PHP projects) and Douglas Crockford created JSON and wrote the most educating book about JavaScript: JavaScript: The Good Parts.
The language you're using doesn't tell me how good a developer you are. That's not because you're a Java developer or a Ruby developer that you know better than Fabien or Douglas about anything. Actually it's more impressive for someone to master a flawed language (and PHP, JavaScript, Perl are majorly flawed) than to master strong languages such as Java.
Finally, to answer directly to DHH and Mike Brock, I know why you took some time to argue with me on this topic. You, more than others, invested a lot in your respective language and you have all the reasons in the world to try to convince people it is better than PHP, Python, JavaScript or Perl. Lincoln's article is just about proselytising Java.
The more people you convince other languages suck, the more product/expertise in your preferred language you're gonna sell to them.
But face it guys. PHP powers much more web sites than Ruby and Java:
And against the odds - and Java applets, VBScript and Flash - JavaScript, as flawed as it is, is the de-facto browser's language (I'm not convinced yet it's an appropriate server-side language but node.js and SilkJS are promising).
Your languages are strong but not that strong.
That's the last time I'll spend time on this holy war of languages. We got the same argument with web browsers and operating systems before that. And actual religions before that.
People tend to take offense when you criticise their preferred language the same way they'd take offence if you criticised their culture or religion. So don't do it, that's petty, mean and counter-productive.
You'd better spend your time developing great apps instead, whatever language you use.
Just a short post to announce that I'm working on a new open-source project called GitRepos.
This is a web tool to self-host Git repositories. Kind of like Bitbucket and Github but on your own Linux server. You'll be able to set up your SSH keys to access your Git repositories, add contributors to your projects and more.
I want to be clear, this is not another Github. Ok it's sort of a Github Enterprise but free and open-source.
I've just started the project on my spare time and I may have (or not) more time to work on it in the coming weeks. The project is based on the Silex PHP micro-framework, coded doing TDD (unit and end-to-end tests). The project source is currently hosted on Github. How ironic, I know ;o)
Contributions are welcome. So fork it! Ok not right now, I've only written sign in and log in. Just wait until it's reached the Minimum Viable Product status.
Updates to come here and on Github. In the meantime, if you're interested in the project or have questions, don't hesitate leave a comment here.