Compare commits
	
		
			83 Commits
		
	
	
		
			sqla-i18n-
			...
			notebook
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6c90829db0 | |||
| b365d200e0 | |||
| a016cb2eda | |||
| 23d0f648a0 | |||
| 4fe42f17ad | |||
| 554e5f9404 | |||
| 0419d61826 | |||
| 2ae87f1b4f | |||
| 239281cc72 | |||
| 4aea8cd85b | |||
| 96a563dc8b | |||
| 79c803588f | |||
| e3e59e37f7 | |||
| d233c3438f | |||
| 43a592fda4 | |||
| dbf536c74b | |||
| 6b47dd7c4b | |||
| 0728698ed4 | |||
| f784e58fad | |||
| 2569e54fb5 | |||
| a26f5864d4 | |||
| 76c7d92df0 | |||
| 02e1800342 | |||
| edfcc33811 | |||
| 1eb4f392c6 | |||
| 60b2efd112 | |||
| 9c43546e15 | |||
| f1d70d270d | |||
| ce86a83848 | |||
| 523b5e614d | |||
| fc3df48a74 | |||
| 300fdb1e6d | |||
| 18cb0e31c1 | |||
| b10a42866f | |||
| 051fd87fb5 | |||
| 4da1081c40 | |||
| 760a46ed5b | |||
| 9becf4c654 | |||
| 334fa8422d | |||
| d3ec86fce5 | |||
| d55a9fc11a | |||
| 8a76794caf | |||
| 6b39cecf7a | |||
| 434a0062f3 | |||
| c53ba939c6 | |||
| 0fc738b033 | |||
| e66c31c646 | |||
| 67eeac2d0a | |||
| 959c614a1d | |||
| b1366ba1f3 | |||
| a4e732c40d | |||
| db8c8824bf | |||
| 8b7dd42b27 | |||
| d3b11a083a | |||
| 2e179936e5 | |||
| 4429b381d0 | |||
| 7d13a05f32 | |||
| 27196d3109 | |||
| 1249112ef2 | |||
| 9de9dc9fbe | |||
| 2ffdd202f4 | |||
| 21000c1fc5 | |||
| cc72dd525c | |||
| d5c1c942f0 | |||
| 49961a3007 | |||
| 3d452d5640 | |||
| 2ed1e51e64 | |||
| 32924598a9 | |||
| 1bed33d65a | |||
| e0640119ba | |||
| f8d33b6c4c | |||
| 06969b3afc | |||
| b0c029ef57 | |||
| af4fbf2ce8 | |||
| beff57738f | |||
| a0822eb344 | |||
| f123b01463 | |||
| 5eaefd30fb | |||
| e9996808be | |||
| 3a1a8758fb | |||
| 4344328d4f | |||
| 40c0d9f304 | |||
| 0979b150db | 
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,5 @@ | |||||||
| /Gemfile.lock | __pycache__/ | ||||||
| _site/ | /output/ | ||||||
|  | /dat-version/ | ||||||
|  | /ipfs-version/ | ||||||
|  | /gergelypolonkaieu_site.egg-info/ | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								.hyde.el
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								.hyde.el
									
									
									
									
									
								
							| @@ -1,2 +0,0 @@ | |||||||
| (setq hyde/git/remote "origin" |  | ||||||
|       hyde/git/remote-branch "master") |  | ||||||
							
								
								
									
										11
									
								
								404.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								404.md
									
									
									
									
									
								
							| @@ -1,11 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: page |  | ||||||
| title: Not Found |  | ||||||
| permalink: /404.html |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| The page you are looking for is not here. Maybe it was but I have removed it. Most likely it was intentionally. If you think I made a mistake, please tell me. |  | ||||||
|  |  | ||||||
| {% if page.url contains '/akarmi' %} |  | ||||||
| If you are looking for the pictures that used to be here, you should definitely contact me. For reasons. |  | ||||||
| {% endif %} |  | ||||||
							
								
								
									
										74
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | PY?=python3 | ||||||
|  | PELICAN?=pelican | ||||||
|  | PELICANOPTS= | ||||||
|  |  | ||||||
|  | BASEDIR=$(CURDIR) | ||||||
|  | INPUTDIR=$(BASEDIR)/content | ||||||
|  | OUTPUTDIR=$(BASEDIR)/output | ||||||
|  | CONFFILE=$(BASEDIR)/pelicanconf.py | ||||||
|  | PUBLISHCONF=$(BASEDIR)/publishconf.py | ||||||
|  |  | ||||||
|  | DEBUG ?= 0 | ||||||
|  | ifeq ($(DEBUG), 1) | ||||||
|  | 	PELICANOPTS += -D | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | RELATIVE ?= 0 | ||||||
|  | ifeq ($(RELATIVE), 1) | ||||||
|  | 	PELICANOPTS += --relative-urls | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | help: | ||||||
|  | 	@echo 'Makefile for a pelican Web site                                           ' | ||||||
|  | 	@echo '                                                                          ' | ||||||
|  | 	@echo 'Usage:                                                                    ' | ||||||
|  | 	@echo '   make html                           (re)generate the web site          ' | ||||||
|  | 	@echo '   make clean                          remove the generated files         ' | ||||||
|  | 	@echo '   make regenerate                     regenerate files upon modification ' | ||||||
|  | 	@echo '   make publish                        generate using production settings ' | ||||||
|  | 	@echo '   make serve [PORT=8000]              serve site at http://localhost:8000' | ||||||
|  | 	@echo '   make serve-global [SERVER=0.0.0.0]  serve (as root) to $(SERVER):80    ' | ||||||
|  | 	@echo '   make devserver [PORT=8000]          serve and regenerate together      ' | ||||||
|  | 	@echo '   make ssh_upload                     upload the web site via SSH        ' | ||||||
|  | 	@echo '   make rsync_upload                   upload the web site via rsync+ssh  ' | ||||||
|  | 	@echo '                                                                          ' | ||||||
|  | 	@echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html   ' | ||||||
|  | 	@echo 'Set the RELATIVE variable to 1 to enable relative urls                    ' | ||||||
|  | 	@echo '                                                                          ' | ||||||
|  |  | ||||||
|  | html: | ||||||
|  | 	$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) | ||||||
|  |  | ||||||
|  | clean: | ||||||
|  | 	[ ! -d $(OUTPUTDIR) ] || rm -rf $(OUTPUTDIR) | ||||||
|  |  | ||||||
|  | regenerate: | ||||||
|  | 	$(PELICAN) -r $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) | ||||||
|  |  | ||||||
|  | serve: | ||||||
|  | ifdef PORT | ||||||
|  | 	$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) | ||||||
|  | else | ||||||
|  | 	$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | serve-global: | ||||||
|  | ifdef SERVER | ||||||
|  | 	$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b $(SERVER) | ||||||
|  | else | ||||||
|  | 	$(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b 0.0.0.0 | ||||||
|  | endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | devserver: | ||||||
|  | ifdef PORT | ||||||
|  | 	$(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) | ||||||
|  | else | ||||||
|  | 	$(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | publish: | ||||||
|  | 	$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(PUBLISHCONF) $(PELICANOPTS) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | .PHONY: html help clean regenerate serve serve-global devserver publish | ||||||
							
								
								
									
										20
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,2 +1,18 @@ | |||||||
| gergelypolonkai.github.io | # Website of Gergely Polonkai | ||||||
| ========================= |  | ||||||
|  | ## Publishing to the web | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | poetry run pelican content | ||||||
|  | rsync --archive --verbose --human-readable --delete output/ /data/gergely.polonkai.eu/html | ||||||
|  | restorecon -vr /data/gergely.polonkai.eu/html | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ## Publishing to IPFS | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | poetry run pelican -s ipfspublish.py -o ipfs-version content | ||||||
|  | cd ipfs-version | ||||||
|  | ipfs add -r . | ||||||
|  | ipfs name publish --key=gergely.polonkai.eu /ipfs/XXX | ||||||
|  | ``` | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								_config.yml
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								_config.yml
									
									
									
									
									
								
							| @@ -1,19 +0,0 @@ | |||||||
| # Site settings |  | ||||||
| title: Gergely Polonkai |  | ||||||
| email: gergely@polonkai.eu |  | ||||||
| description: "developer, systems engineer and administrator" |  | ||||||
| baseurl: "" |  | ||||||
| url: "http://gergely.polonkai.eu" |  | ||||||
| timezone: Europe/Budapest |  | ||||||
| name: Gergely Polonkai |  | ||||||
| paginate: 10 |  | ||||||
| paginate_path: "/blog/page/:num" |  | ||||||
| exclude: ['README.md', 'Gemfile', 'Gemfile.lock', 'CNAME', ".hyde.el"] |  | ||||||
| include: ['.well-known'] |  | ||||||
| plugins: |  | ||||||
|   - jekyll-gist |  | ||||||
|   - jekyll-paginate |  | ||||||
|  |  | ||||||
| # Build settings |  | ||||||
| markdown: kramdown |  | ||||||
| permalink: pretty |  | ||||||
| @@ -1,42 +0,0 @@ | |||||||
| - text: E-mail |  | ||||||
|   link: mailto:gergely@polonkai.eu |  | ||||||
|   image: email.png |  | ||||||
| - text: Stack Exchange |  | ||||||
|   link: http://stackexchange.com/users/1369500/gergelypolonkai |  | ||||||
|   image: stackexchange.png |  | ||||||
| - text: LinkedIn |  | ||||||
|   link: http://www.linkedin.com/in/gergelypolonkai |  | ||||||
|   image: linkedin.png |  | ||||||
| - text: Skype |  | ||||||
|   link: skype:gergely.polonkai |  | ||||||
|   image: skype.png |  | ||||||
| - text: Facebook |  | ||||||
|   link: http://facebook.com/Polesz |  | ||||||
|   image: facebook.png |  | ||||||
| - text: Google+ |  | ||||||
|   link: https://plus.google.com/+GergelyPolonkai/about |  | ||||||
|   image: google_plus.png |  | ||||||
| - text: Twitter |  | ||||||
|   link: http://twitter.com/GergelyPolonkai |  | ||||||
|   image: twitter.png |  | ||||||
| - text: Tumblr |  | ||||||
|   link: http://gergelypolonkai.tumblr.com |  | ||||||
|   image: tumblr.png |  | ||||||
| - text: deviantArt |  | ||||||
|   link: http://gergelypolonkai.deviantart.com |  | ||||||
|   image: deviantart.png |  | ||||||
| - text: Hashnode |  | ||||||
|   link: https://hashnode.com/@gergelypolonkai |  | ||||||
|   image: hashnode.png |  | ||||||
| - text: Keybase |  | ||||||
|   link: https://keybase.io/gergelypolonkai |  | ||||||
|   image: keybase.png |  | ||||||
| - text: Liberapay |  | ||||||
|   link: https://liberapay.com/gergelypolonkai |  | ||||||
|   image: liberapay.png |  | ||||||
| - text: Mastodon |  | ||||||
|   link: https://social.polonkai.eu/@gergely |  | ||||||
|   image: mastodon.png |  | ||||||
| - text: Pay me a coffee |  | ||||||
|   link: https://paypal.me/GergelyPolonkai/250 |  | ||||||
|   image: paypal.png |  | ||||||
							
								
								
									
										1738
									
								
								_data/symbolon.json
									
									
									
									
									
								
							
							
						
						
									
										1738
									
								
								_data/symbolon.json
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,15 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: post |  | ||||||
| title: "GtkActionable in action" |  | ||||||
| author: |  | ||||||
|   name: "Gergely Polonkai" |  | ||||||
|   email: "gergely@polonkai.eu" |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I have seen several people (including myself) struggling with |  | ||||||
| disabling/enabling menu items, toolbar buttons and similar UI |  | ||||||
| interfaces based on different conditions. It gets even worse if there |  | ||||||
| are multiple representations of the same action in the same |  | ||||||
| application, e.g. a menu item and a toolbar button exists for the same |  | ||||||
| action. But with GTK+ 3.4, we have GtkAction, which is exactly for |  | ||||||
| this kind of situations. |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: post |  | ||||||
| title: "Measuring code coverage with codecov for libtool projects" |  | ||||||
| author: |  | ||||||
|   name: "Gergely Polonkai" |  | ||||||
|   email: "gergely@polonkai.eu" |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I have recently found [codecov][https://codecov.io/]; they offer free |  | ||||||
| services for public GitHub projects. As I have recently started writing |  | ||||||
| tests for my SWE-GLib project, I decided to give it a go. Things are not |  | ||||||
| this easy if you use GNU Autotools and libtool, though… |  | ||||||
|  |  | ||||||
| The problem here is that these tools generate output under `src/.libs/` |  | ||||||
| (given that your sources are under `src/`) and `gcov` has hard times |  | ||||||
| finding the coverage data files. Well, at least in the codecov |  | ||||||
| environment, it works fine on my machine. |  | ||||||
| @@ -1,326 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: post |  | ||||||
| title: "Writing a GNOME Shell extension" |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I could not find a good tutorial on how to write a GNOME Shell |  | ||||||
| extension. There is a so called step by step |  | ||||||
| [instruction list](https://wiki.gnome.org/Projects/GnomeShell/Extensions/StepByStepTutorial) |  | ||||||
| on how to do it, but it has its flaws, including grammar and clearance. |  | ||||||
| As I wanted to create an extension for my SWE GLib library to display |  | ||||||
| the current position of some planets, I dug into existing (and working) |  | ||||||
| extensions’ source code and made up something. Comments welcome! |  | ||||||
|  |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| GNOME Shell extensions are written in JavaScript and are interpreted |  | ||||||
| by [GJS](https://wiki.gnome.org/action/show/Projects/Gjs). Using |  | ||||||
| introspected libraries from JavaScript is not a problem for me (see |  | ||||||
| SWE GLib’s |  | ||||||
| [Javascript example](https://github.com/gergelypolonkai/swe-glib/blob/master/examples/basic.js); |  | ||||||
| it’s not beautiful, but it’s working), but wrapping your head around |  | ||||||
| the Shell’s concept can take some time. |  | ||||||
|  |  | ||||||
| The Shell is a Clutter stage, and all the buttons (including the |  | ||||||
| top-right “Activities” button) are actors on this stage. You can add |  | ||||||
| practically anything to the Shell panel that you can add to a Clutter |  | ||||||
| stage. |  | ||||||
|  |  | ||||||
| The other thing to remember is the lifecycle of a Shell |  | ||||||
| extension. After calling `init()`, there are two ways forward: you |  | ||||||
| either use a so called extension controller, or plain old JavaScript |  | ||||||
| functions `enable()` and `disable()`; I will go on with the former |  | ||||||
| method for reasons discussed later. |  | ||||||
|  |  | ||||||
| If you are fine with the `enable()`/`disable()` function version, you |  | ||||||
| can ease your job with the following command: |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| gnome-shell-extension-tool --create-extension |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| This will ask you a few parameters and create the necessary files for |  | ||||||
| you. On what these parameters should look like, please come with me to |  | ||||||
| the next section. |  | ||||||
|  |  | ||||||
| ## Placement and naming |  | ||||||
|  |  | ||||||
| Extensions reside under `$HOME/.local/share/gnome-shell/extensions`, |  | ||||||
| where each of them have its own directory. The directory name has to be |  | ||||||
| unique, of course; to achieve this, they are usually the same as the |  | ||||||
| UUID of the extension. |  | ||||||
|  |  | ||||||
| The UUID is a string of alphanumeric characters, with some extras added. |  | ||||||
| Generally, it should match this regular expression: |  | ||||||
| `^[-a-zA-Z0-9@._]+$`. The convention is to use the form |  | ||||||
| `extension-name@author-id`, e.g. `Planets@gergely.polonkai.eu`. Please |  | ||||||
| see |  | ||||||
| [this link](https://wiki.gnome.org/Projects/GnomeShell/Extensions/UUIDGuidelines) |  | ||||||
| for some more information about this. |  | ||||||
|  |  | ||||||
| ## Anatomy of an extension |  | ||||||
|  |  | ||||||
| Extensions consist of two main parts, `metadata.json` and |  | ||||||
| `extension.js`. |  | ||||||
|  |  | ||||||
| The `metadata.json` file contains compatibility information and, well, |  | ||||||
| some meta data: |  | ||||||
|  |  | ||||||
| ```json |  | ||||||
| { |  | ||||||
|     "shell-version": ["3.18"], |  | ||||||
|     "uuid": "planets@gergely.polonkai.eu", |  | ||||||
|     "name": "Planets", |  | ||||||
|     "description": "Display current planet positions" |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Here, `shell-version` must contain all versions of GNOME Shell that is |  | ||||||
| known to load and display your extension correctly. You can insert minor |  | ||||||
| versions here, like I did, or exact version numbers, like `3.18.1`. |  | ||||||
|  |  | ||||||
| In the `extension.js` file, which contains the actual extension code, |  | ||||||
| the only thing you actually need is an `init()` function: |  | ||||||
|  |  | ||||||
| ```javascript |  | ||||||
| function init(extensionMeta) { |  | ||||||
|     // Do whatever it takes to initialize your extension, like |  | ||||||
|     // initializing the translations. However, never do any widget |  | ||||||
|     // magic here yet. |  | ||||||
|  |  | ||||||
|     // Then return the controller object |  | ||||||
|     return new ExtensionController(extensionMeta); |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## Extension controller |  | ||||||
|  |  | ||||||
| So far so good, but what is this extension controller thing? It is an |  | ||||||
| object which is capable of managing your GNOME Shell extension. Whenever |  | ||||||
| the extension is loaded, its `enable()` method is called; when the |  | ||||||
| extension is unloaded, you guessed it, the `disable()` method gets |  | ||||||
| called. |  | ||||||
|  |  | ||||||
| ```javascript |  | ||||||
| function ExtensionController(extensionMeta) { |  | ||||||
|     return { |  | ||||||
|         extensionMeta: extensionMeta, |  | ||||||
|         extension: null, |  | ||||||
|  |  | ||||||
|         enable: function() { |  | ||||||
|             this.extension = new PlanetsExtension(this.extensionMeta); |  | ||||||
|  |  | ||||||
|             Main.panel.addToStatusArea("planets", |  | ||||||
|                                        this.extension, |  | ||||||
|                                        0, "right"); |  | ||||||
|         }, |  | ||||||
|  |  | ||||||
|         disable: function() { |  | ||||||
|             this.extension.actor.destroy(); |  | ||||||
|             this.extension.destroy(); |  | ||||||
|  |  | ||||||
|             this.extension = null; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| This controller will create a new instance of the `PlanetsExtension` |  | ||||||
| class and add it to the panel’s right side when loaded. Upon |  | ||||||
| unloading, the extension’s actor gets destroyed (which, as you will |  | ||||||
| see later, gets created behind the scenes, not directly by us), |  | ||||||
| together with the extension itself. Also, for safety measures, the |  | ||||||
| extension is set to `null`. |  | ||||||
|  |  | ||||||
| ## The extension |  | ||||||
|  |  | ||||||
| The extension is a bit more tricky, as, for convenience reasons, it |  | ||||||
| should extend an existing panel widget type. |  | ||||||
|  |  | ||||||
| ```javascript |  | ||||||
| function PlanetsExtension(extensionMeta) { |  | ||||||
|     this._init(extensionMeta); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| PlanetsExtension.prototype = { |  | ||||||
|     __proto__ = PanelMenu.Button.prototype, |  | ||||||
|  |  | ||||||
|     _init: function(extensionMeta) { |  | ||||||
|         PanelMenu.Button.prototype._init.call(this, 0.0); |  | ||||||
|  |  | ||||||
|         this.extensionMeta = extensionMeta; |  | ||||||
|  |  | ||||||
|         this.panelContainer = new St.BoxLayout({style_class: 'panel-box'}); |  | ||||||
|         this.actor.add_actor(this.panelContainer); |  | ||||||
|         this.actor.add_style_class_name('panel-status-button'); |  | ||||||
|  |  | ||||||
|         this.panelLabel = new St.Label({ |  | ||||||
|             text: 'Loading', |  | ||||||
|             y_align: Clutter.ActorAlign.CENTER |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.panelContainer.add(this.panelLabel); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Here we extend the Button class of panelMenu, so we will be able to do |  | ||||||
| some action upon activate. |  | ||||||
|  |  | ||||||
| The only parameter passed to the parent’s `_init()` function is |  | ||||||
| `menuAlignment`, with the value `0.0`, which is used to position the |  | ||||||
| menu arrow. (_Note: I cannot find any documentation on this, but it |  | ||||||
| seems that with the value `0.0`, a menu arrow is not added._) |  | ||||||
|  |  | ||||||
| The extension class in its current form is capable of creating the |  | ||||||
| actual panel button displaying the text “Loading” in its center. |  | ||||||
|  |  | ||||||
| ## Loading up the extension |  | ||||||
|  |  | ||||||
| Now with all the necessary import lines added: |  | ||||||
|  |  | ||||||
| ```javascript |  | ||||||
| // The PanelMenu module that contains Button |  | ||||||
| const PanelMenu = imports.ui.panelMenu; |  | ||||||
| // The St class that contains lots of UI functions |  | ||||||
| const St = imports.gi.St; |  | ||||||
| // Clutter, which is used for displaying everything |  | ||||||
| const Clutter = imports.gi.Clutter; |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| As soon as this file is ready, you can restart your Shell (press |  | ||||||
| Alt-F2 and enter the command `r`), and load the extension with |  | ||||||
| e.g. the GNOME Tweak Tool. You will see the Planets button on the |  | ||||||
| right. This little label showing the static text “Planets”, however, |  | ||||||
| is pretty boring, so let’s add some action. |  | ||||||
|  |  | ||||||
| ## Adding some periodical change |  | ||||||
|  |  | ||||||
| Since the planets’ position continuously change, we should update our |  | ||||||
| widget every minute or so. Let’s patch our `_init()` a bit: |  | ||||||
|  |  | ||||||
| ```javascript |  | ||||||
| this.last_update = 0; |  | ||||||
|  |  | ||||||
| MainLoop.timeout_add(1, Lang.bind(this, function() { |  | ||||||
|     this.last_update++; |  | ||||||
|     this.panelLabel.set_text("Update_count: " + this.last_update); |  | ||||||
| })) |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| This, of course, needs a new import line for `MainLoop` to become available: |  | ||||||
|  |  | ||||||
| ```javascript |  | ||||||
| const MainLoop = imports.mainloop; |  | ||||||
| const Lang = imports.lang; |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Now if you restart your Shell, your brand new extension will increase |  | ||||||
| its counter every second. This, however, presents some problems. |  | ||||||
|  |  | ||||||
| SWE GLib queries can sometimes be expensive, both in CPU and disk |  | ||||||
| operations, so updating our widget every second may present problems. |  | ||||||
| Also, planets don’t go **that** fast. We may update our timeout value |  | ||||||
| from `1` to `60` or something, but why don’t just give our user a chance |  | ||||||
| to set it? |  | ||||||
|  |  | ||||||
| ## Introducing settings |  | ||||||
|  |  | ||||||
| Getting settings from `GSettings` is barely straightforward, especially |  | ||||||
| for software installed in a non-GNOME directory (which includes |  | ||||||
| extensions). To make our lives easier, I copied over a |  | ||||||
| [convenience library](https://github.com/projecthamster/shell-extension/blob/master/convenience.js) |  | ||||||
| from the [Hamster project](https://projecthamster.wordpress.com/)’s |  | ||||||
| extension, originally written by Giovanni Campagna. The relevant |  | ||||||
| function here is `getSettings()`: |  | ||||||
|  |  | ||||||
| ```javascript |  | ||||||
| /** |  | ||||||
|  * getSettings: |  | ||||||
|  * @schema: (optional): the GSettings schema id |  | ||||||
|  * |  | ||||||
|  * Builds and return a GSettings schema for @schema, using schema files |  | ||||||
|  * in extensionsdir/schemas. If @schema is not provided, it is taken from |  | ||||||
|  * metadata['settings-schema']. |  | ||||||
|  */ |  | ||||||
| function getSettings(schema) { |  | ||||||
|     let extension = ExtensionUtils.getCurrentExtension(); |  | ||||||
|  |  | ||||||
|     schema = schema || extension.metadata['settings-schema']; |  | ||||||
|  |  | ||||||
|     const GioSSS = Gio.SettingsSchemaSource; |  | ||||||
|  |  | ||||||
|     // check if this extension was built with "make zip-file", and thus |  | ||||||
|     // has the schema files in a subfolder |  | ||||||
|     // otherwise assume that extension has been installed in the |  | ||||||
|     // same prefix as gnome-shell (and therefore schemas are available |  | ||||||
|     // in the standard folders) |  | ||||||
|     let schemaDir = extension.dir.get_child('schemas'); |  | ||||||
|     let schemaSource; |  | ||||||
|     if (schemaDir.query_exists(null)) |  | ||||||
|         schemaSource = GioSSS.new_from_directory(schemaDir.get_path(), |  | ||||||
|                                                  GioSSS.get_default(), |  | ||||||
|                                                  false); |  | ||||||
|     else |  | ||||||
|         schemaSource = GioSSS.get_default(); |  | ||||||
|  |  | ||||||
|     let schemaObj = schemaSource.lookup(schema, true); |  | ||||||
|     if (!schemaObj) |  | ||||||
|         throw new Error('Schema ' + schema + ' could not be found for extension ' |  | ||||||
|                         + extension.metadata.uuid + '. Please check your installation.'); |  | ||||||
|  |  | ||||||
|     return new Gio.Settings({ settings_schema: schemaObj }); |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| You can either incorporate this function into your `extension.js` file, |  | ||||||
| or just use `convenience.js` file like I (and the Hamster applet) did |  | ||||||
| and import it: |  | ||||||
|  |  | ||||||
| ```javascript |  | ||||||
| const ExtensionUtils = imports.misc.extensionUtils; |  | ||||||
| const Me = ExtensionUtils.getCurrentExtension; |  | ||||||
| const Convenience = Me.imports.convenience; |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Now let’s create the settings definition. GSettings schema files are XML |  | ||||||
| files. We want to add only one settings for now, the refresh interval. |  | ||||||
|  |  | ||||||
| ```xml |  | ||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <schemalist> |  | ||||||
|     <schema id="org.gnome.shell.extensions.planets" path="/org/gnome/shell/extensions/planets/"> |  | ||||||
|         <key name="refresh-interval" type="i"> |  | ||||||
|             <default>30</default> |  | ||||||
|             <summary>Refresh interval of planet data</summary> |  | ||||||
|             <description>Interval in seconds. Sets how often the planet positions are recalculated. Setting this too low (e.g. below 30) may raise performance issues.</description> |  | ||||||
|         </key> |  | ||||||
|     </schema> |  | ||||||
| </schemalist> |  | ||||||
| ``` |  | ||||||
| you need to compile these settings with |  | ||||||
|  |  | ||||||
|     glib-compile-schemas --strict schemas/ |  | ||||||
|  |  | ||||||
| Now let’s utilize this new setting. In the extension’s `_init()` |  | ||||||
| function, add the following line: |  | ||||||
|  |  | ||||||
| ```javascript |  | ||||||
| this._settings = Convenience.getSettings(); |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| And, for `getSettings()` to work correctly, we also need to extend our |  | ||||||
| `metadata.json` file: |  | ||||||
|  |  | ||||||
| ```json |  | ||||||
|     "settings-schema": "planets" |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| After another restart (please, GNOME guys, add an option to reload |  | ||||||
| extensions!), your brand new widget will refresh every 30 seconds. |  | ||||||
|  |  | ||||||
| ## Displaying the planet positions |  | ||||||
|  |  | ||||||
| ## The settings panel |  | ||||||
|  |  | ||||||
| ## Start an application |  | ||||||
| @@ -1,67 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Lessens you learn while writing an SDK" |  | ||||||
| date:      2016-03-19 12:34:56 |  | ||||||
| tags:      [development] |  | ||||||
| published: false |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| In the last few months I’ve been working on a GLib based SDK for |  | ||||||
| client applications that want to communicate with a Matrix.org |  | ||||||
| homeserver. |  | ||||||
|  |  | ||||||
| For whoever doesn’t know it, Matrix is a decentralized network of |  | ||||||
| servers (Homeservers). Clients can connect to them via HTTP and send |  | ||||||
| messages (events, in Matrix terminology) to each other. They are |  | ||||||
| called events because these messages can be pretty much anything from |  | ||||||
| instant messages through automated notifications to files or, well, |  | ||||||
| actual events (such as a vCalendar); anything that you can serialize |  | ||||||
| to JSON can go through this network. |  | ||||||
|  |  | ||||||
| My original intention was to integrate Matrix based chat into |  | ||||||
| Telepathy, a DBus based messaging framework used by e.g. the GNOME |  | ||||||
| desktop (more specifically Empathy, GNOME's chat client.) After |  | ||||||
| announcing my plans among the Matrix devs, I quickly learned some |  | ||||||
| things: |  | ||||||
|  |  | ||||||
| 1. they are more than open to any development ideas |  | ||||||
| 1. they really wanted to see this working |  | ||||||
| 1. they would have been happy if there were a GLib or Qt based SDK |  | ||||||
|  |  | ||||||
| With my (far from complete) knowledge in GLib I decided to move on |  | ||||||
| with this last point, hoping that it will help me much when I finally |  | ||||||
| implement the Telepathy plugin. |  | ||||||
|  |  | ||||||
| ## Matrix devs are open minded |  | ||||||
|  |  | ||||||
| What I learned very quickly is that Matrix devs are very open minded |  | ||||||
| folks from different parts of the world. They are all individuals with |  | ||||||
| their own ideas, experiences and quirks, yet, when it comes to that, |  | ||||||
| they steer towards their goals as a community. Thus, getting |  | ||||||
| additional information from them while reading the spec was super |  | ||||||
| easy. |  | ||||||
|  |  | ||||||
| ## The specification is easy to understand |  | ||||||
|  |  | ||||||
| Except when it is not. For these cases, see the previous point. |  | ||||||
|  |  | ||||||
| Jokes asidu, anyone who worked with communications protocols or JSON |  | ||||||
| APIs before can get along with it fast. The endpoints are all |  | ||||||
| documented, and if something is unclear, they are happy to help |  | ||||||
| (especially if you patch up the spec afterwards.) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Copying the SDK for a different language is not (always) what you want |  | ||||||
|  |  | ||||||
| I started my SDK in C, trying to mimic the Python SDK. This was a |  | ||||||
| double fail: the Python SDK was a volatile WiP, and C and Python are |  | ||||||
| fundamentally different. |  | ||||||
|  |  | ||||||
| During the upcoming weeks this became clear and I switched to the Vala |  | ||||||
| language. It is much easier to write GObject based stuff in Vala, |  | ||||||
| although I had to fall back to C to get some features working. I also |  | ||||||
| planned and implemented a more object oriented API, which is easier to |  | ||||||
| use in the GObject world. |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| <p> |  | ||||||
|     Gergely Polonkai is a systems engineer of a telco company, and |  | ||||||
|     also a freelancer self- and software developer. |  | ||||||
| </p> |  | ||||||
|  |  | ||||||
| <p> |  | ||||||
|     He is learning about different IT subjects since the late |  | ||||||
|     1990s. These include web development, application building, |  | ||||||
|     systems engineering, IT security and many others. He also dug his |  | ||||||
|     nose deeply into free software, dealing with different types of |  | ||||||
|     Linux and its applications, |  | ||||||
|     while also writing and contributing to some open source projects. |  | ||||||
| </p> |  | ||||||
|  |  | ||||||
| <p> |  | ||||||
|     On this site he is writing posts about different stuff he faces |  | ||||||
|     during work (oh my, yet another IT solutions blog), hoping they |  | ||||||
|     can help others with their job, or just to get along with their |  | ||||||
|     brand new netbook that shipped with Linux. |  | ||||||
| </p> |  | ||||||
|  |  | ||||||
| <p> |  | ||||||
|     “I believe one can only achieve success if they follow their own |  | ||||||
|     instincts and listen to, but not bend under others’ opinions. If |  | ||||||
|     you change your course just because someone says so, you are |  | ||||||
|     following their instincts, not yours.” |  | ||||||
| </p> |  | ||||||
| @@ -1,49 +0,0 @@ | |||||||
| <article class="{% if page.post_listing %}col-sm-5 col-md-6 {% endif%}post"> |  | ||||||
| {% if page.post_listing %} |  | ||||||
|     <ul class="list-inline"> |  | ||||||
|         <li class="col-md-8"> |  | ||||||
| {% endif %} |  | ||||||
|             <header class="post-header"> |  | ||||||
| {% if page.tag %} |  | ||||||
|                 <h5> |  | ||||||
| {% else %} |  | ||||||
|                 <h3> |  | ||||||
| {% endif %} |  | ||||||
| {% if page.post_listing %} |  | ||||||
| <a href="{{post.url | prepend: site.baseurl}}"> |  | ||||||
| {% endif %} |  | ||||||
| {{post.title}} |  | ||||||
| {% if page.post_listing %} |  | ||||||
| </a> |  | ||||||
| {% endif %} |  | ||||||
| {% if layout.render_post %} |  | ||||||
|                     <div class="plusone-container"><div class="g-plusone" data-annotation="inline" data-size="small" data-width="300"></div></div> |  | ||||||
| {% endif %} |  | ||||||
| {% if page.tag %} |  | ||||||
|                 </h5> |  | ||||||
| {% else %} |  | ||||||
|                 </h3> |  | ||||||
| {% endif %} |  | ||||||
|                 <div class="meta pull-left"> |  | ||||||
|                     {{post.author.name}} |  | ||||||
|                 </div> |  | ||||||
|                 <div class="meta pull-right"> |  | ||||||
|                     {{post.date | date: "%b %-d, %Y :: %H:%M"}} |  | ||||||
|                 </div> |  | ||||||
|                 <div class="clearfix"></div> |  | ||||||
|             </header> |  | ||||||
|  |  | ||||||
|             <main> |  | ||||||
| {% if layout.render_post %} |  | ||||||
|                 {{content}} |  | ||||||
| {% else %} |  | ||||||
|                 {{post.excerpt}} |  | ||||||
| {% endif %} |  | ||||||
|             </main> |  | ||||||
|  |  | ||||||
| {% include tag-link.html %} |  | ||||||
| {% if layout.post_listing %} |  | ||||||
|         </li> |  | ||||||
|     </ul> |  | ||||||
| {% endif %} |  | ||||||
| </article> |  | ||||||
| @@ -1,14 +0,0 @@ | |||||||
| <div id="disqus_thread"></div> |  | ||||||
| <script type="text/javascript"> |  | ||||||
|     var disqus_shortname = 'gergelypolonkai'; |  | ||||||
|  |  | ||||||
|     (function() { |  | ||||||
|         var dsq = document.createElement('script'); |  | ||||||
|         dsq.type = 'text/javascript'; |  | ||||||
|         dsq.async = true; |  | ||||||
|         dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; |  | ||||||
|         (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); |  | ||||||
|     })(); |  | ||||||
| </script> |  | ||||||
| <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript> |  | ||||||
| <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a> |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
|         <meta charset="UTF-8"> |  | ||||||
|         <meta name="viewport" content="width=device-width, initial-scale=1.0"> |  | ||||||
|         <meta name="keywords" content="{{page.keywords}}"> |  | ||||||
|         <meta name="description" content="Personal page of Gergely Polonkai"> |  | ||||||
|         <title>Gergely Polonkai{% if page.title %}: {{page.title}}{% endif %}</title> |  | ||||||
|  |  | ||||||
|         <link rel="icon" type="image/x-icon" href="{{'/favicon.ico' | prepend: site.baseurl}}"> |  | ||||||
|         <link href="https://fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,400italic,600,600italic,700,700italic,800,800italic" rel="stylesheet" type="text/css"> |  | ||||||
|         <link rel="alternate" type="application/rss+xml" title="Gergely Polonkai's Blog - RSS Feed" href="{{site.url}}/blog/atom.xml"> |  | ||||||
|         <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"> |  | ||||||
|         <link rel="stylesheet" href="{{'/css/style.css' | prepend: site.baseurl}}"> |  | ||||||
|         <link href="https://cdnjs.cloudflare.com/ajax/libs/jquery.terminal/1.6.3/css/jquery.terminal.min.css" rel="stylesheet"/> |  | ||||||
|  |  | ||||||
|         <script type="text/javascript" src="//code.jquery.com/jquery-2.1.3.min.js"></script> |  | ||||||
|         <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script> |  | ||||||
|         <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.terminal/1.6.3/js/jquery.terminal.min.js"></script> |  | ||||||
| @@ -1,35 +0,0 @@ | |||||||
|             <div class="navbar navbar-inverse navbar-fixed-top"> |  | ||||||
|                 <div class="container-fluid"> |  | ||||||
|                     <div class="navbar-header"> |  | ||||||
|                         <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#gp-navbar"> |  | ||||||
|                             <span class="icon-bar"></span> |  | ||||||
|                             <span class="icon-bar"></span> |  | ||||||
|                             <span class="icon-bar"></span> |  | ||||||
|                         </button> |  | ||||||
|                         <a class="navbar-brand" href="{{'/' | prepend: site.baseurl}}"><img src="{{'/images/profile.svg' | prepend: site.baseurl}}" alt="Gergely Polonkai" style="background-color: white; height: 45px; margin-top: -13px;"></a> |  | ||||||
| {% if page.url != '/' %} |  | ||||||
|                         <a class="navbar-brand" href="{{'/' | prepend: site.baseurl}}">Gergely Polonkai</a> |  | ||||||
| {% endif %} |  | ||||||
|                     </div> |  | ||||||
|                     <div class="collapse navbar-collapse" id="gp-navbar"> |  | ||||||
|                         <ul class="nav navbar-nav"> |  | ||||||
|                             <li><a href="{{'/blog' | prepend: site.baseurl}}">Blog</a></li> |  | ||||||
|                             <li><a href="{{'/resume' | prepend: site.baseurl}}">Resume</a></li> |  | ||||||
|                             <li><a href="{{'/stories' | prepend: site.baseurl}}">Stories</a></li> |  | ||||||
|                         </ul> |  | ||||||
|                         <ul class="nav navbar-nav navbar-right"> |  | ||||||
|                             <li><a href="https://about.me/gergely.polonkai">about.me</a></li> |  | ||||||
|                             <li><a href="{{'/disclaimer' | prepend: site.baseurl}}">Disclaimer</a></li> |  | ||||||
|                             <li class="dropdown"> |  | ||||||
|                                 <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="glyphicon glyphicon-pencil"></span> Contact me <span class="caret"></span></a> |  | ||||||
|                                 <ul class="dropdown-menu" role="menu"> |  | ||||||
| {% for contact in site.data.contacts %} |  | ||||||
|                                     <li><a href="{{contact.link}}" target="_blank"><img src="{{'/images/contact/' | prepend: site.baseurl | append: contact.image}}" alt="" /> {{contact.text}}</a></li> |  | ||||||
| {% endfor %} |  | ||||||
|                                     <li><a href="{{'/blog/atom.xml' | prepend: site.baseurl}}"><img src="{{'/images/contact/feed.png' | prepend: site.baseurl}}" alt="" /> RSS Feed</a></li> |  | ||||||
|                                 </ul> |  | ||||||
|                             </li> |  | ||||||
|                         </ul> |  | ||||||
|                     </div> |  | ||||||
|                 </div> |  | ||||||
|             </div> |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| <nav> |  | ||||||
|     <ul class="pagination"> |  | ||||||
|         <li{% if paginator.previous_page == null %} class="disabled"{% endif %}> |  | ||||||
|             <a href="{{paginator.previous_page_path | prepend: site.baseurl | replace: '//', '/'}}" aria-label="Previous page"> |  | ||||||
|                 <span aria-hidden="true">«</span> |  | ||||||
|             </a> |  | ||||||
|         </li> |  | ||||||
| {% for page in (1..paginator.total_pages) %} |  | ||||||
|         <li{% if paginator.page == page %} class="active"{% endif %}><a href="{% if page == 1 %}{{'/blog' | prepend: site.baseurl}}{% else %}{{site.paginate_path | prepend: site.baseurl | replace: '//', '/' | replace: ':num', page}}{% endif %}">{{page}}</a></li> |  | ||||||
| {% endfor %} |  | ||||||
|         <li{% if paginator.next_page == null %} class="disabled"{% endif %}> |  | ||||||
|             <a href="{{paginator.next_page_path | prepend: site.baseurl | replace: '//', '/'}}" aria-label="Next page"> |  | ||||||
|                 <span aria-hidden="true">»</span> |  | ||||||
|             </a> |  | ||||||
|         </li> |  | ||||||
|     </ul> |  | ||||||
| </nav> |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| <div class="container-fluid"> |  | ||||||
| {% for post in posts limit: post_limit %} |  | ||||||
| {%     capture counter %}{% cycle 'odd', 'even' %}{% endcapture %} |  | ||||||
| {%     include blog-post.html %} |  | ||||||
| {%     if counter == 'even' %} |  | ||||||
|     <div class="clearfix"></div> |  | ||||||
| {%     endif %} |  | ||||||
| {% endfor %} |  | ||||||
| </div> |  | ||||||
| @@ -1,11 +0,0 @@ | |||||||
| {% capture tagsize %}{{post.tags | size}}{% endcapture %} |  | ||||||
| {% if tagsize != '0' %} |  | ||||||
|             <footer> |  | ||||||
|                 <p class="article-tags"> |  | ||||||
| {%     for tag in post.tags %} |  | ||||||
|                     <a href="{{tag | prepend: '/blog/tag/' | prepend: site.baseurl}}" class="tag-label">{{tag}}</a> |  | ||||||
| {%     endfor %} |  | ||||||
|                 </p> |  | ||||||
|                 <br class="clearfix"> |  | ||||||
|             </footer> |  | ||||||
| {% endif %} |  | ||||||
| @@ -1,130 +0,0 @@ | |||||||
| <!DOCTYPE html> |  | ||||||
| <html> |  | ||||||
|     <head> |  | ||||||
| {% include head.html %} |  | ||||||
|     </head> |  | ||||||
|     <body> |  | ||||||
| {% include header.html %} |  | ||||||
|         <div class="container" id="main-container"> |  | ||||||
|  |  | ||||||
|             {{content}} |  | ||||||
|  |  | ||||||
| {% if page.name != 'about.html' %} |  | ||||||
|             <div class="well well-sm small"> |  | ||||||
|                 <div class="pull-left" id="about-well-image"> |  | ||||||
|                     <a href="{{ site_url }}/about/"> |  | ||||||
|                         <img src="{{'/images/profile.svg' | prepend: site.baseurl}}" alt=""> |  | ||||||
|                     </a> |  | ||||||
|                 </div> |  | ||||||
| {%     include about.html %} |  | ||||||
|                 <div class="clearfix"></div> |  | ||||||
|             </div> |  | ||||||
| {% endif %} |  | ||||||
|         </div> |  | ||||||
|         <script type="text/javascript"> |  | ||||||
|          $(document).ready(function() { |  | ||||||
|              $('#tagcloud-button').click(function() { |  | ||||||
|                  $('#tag-cloud').toggle('slow'); |  | ||||||
|              }); |  | ||||||
|          }); |  | ||||||
|  |  | ||||||
|          (function() { |  | ||||||
|              var po = document.createElement('script'); |  | ||||||
|              po.type = 'text/javascript'; |  | ||||||
|              po.async = true; |  | ||||||
|              po.src = 'https://apis.google.com/js/client:plusone.js?onload=start'; |  | ||||||
|  |  | ||||||
|              var s = document.getElementsByTagName('script')[0]; |  | ||||||
|              s.parentNode.insertBefore(po, s); |  | ||||||
|          })(); |  | ||||||
|  |  | ||||||
|          (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); |  | ||||||
|  |  | ||||||
|          ga('create', 'UA-43569023-1', 'polonkai.eu'); |  | ||||||
|          ga('send', 'pageview'); |  | ||||||
|  |  | ||||||
|          jQuery.extend_if_has = function(desc, source, array) { |  | ||||||
|              for (var i=array.length;i--;) { |  | ||||||
|                  if (typeof source[array[i]] != 'undefined') { |  | ||||||
|                      desc[array[i]] = source[array[i]]; |  | ||||||
|                  } |  | ||||||
|              } |  | ||||||
|              return desc; |  | ||||||
|          }; |  | ||||||
|  |  | ||||||
|          (function($) { |  | ||||||
|              $.fn.tilda = function(eval, options) { |  | ||||||
|                  if ($('body').data('tilda')) { |  | ||||||
|                      return $('body').data('tilda').terminal; |  | ||||||
|                  } |  | ||||||
|                  this.addClass('tilda'); |  | ||||||
|                  options = options || {}; |  | ||||||
|                  eval = eval || function(command, term) { |  | ||||||
|                      term.echo("you don't set eval for tilda"); |  | ||||||
|                  }; |  | ||||||
|                  var settings = { |  | ||||||
|                      prompt: 'guest@gergely.polonkai.eu> ', |  | ||||||
|                      name: 'tilda', |  | ||||||
|                      height: 400, |  | ||||||
|                      enabled: false, |  | ||||||
|                      greetings: 'Welcome to my Terminal.  Type `help\' to list the available commands.\n\nPowered by http://terminal.jcubic.pl', |  | ||||||
|                      keypress: function(e) { |  | ||||||
|                          if (e.which == 96) { |  | ||||||
|                              return false; |  | ||||||
|                          } |  | ||||||
|                      } |  | ||||||
|                  }; |  | ||||||
|                  if (options) { |  | ||||||
|                      $.extend(settings, options); |  | ||||||
|                  } |  | ||||||
|                  this.append('<div class="td"></div>'); |  | ||||||
|                  var self = this; |  | ||||||
|                  self.terminal = this.find('.td').terminal(eval, settings); |  | ||||||
|                  var focus = false; |  | ||||||
|                  $(document.documentElement).keypress(function(e) { |  | ||||||
|                      console.log(e); |  | ||||||
|                      if (e.which == 96) { |  | ||||||
|                          self.slideToggle('fast'); |  | ||||||
|                          self.terminal.focus(focus = !focus); |  | ||||||
|                          self.terminal.attr({ |  | ||||||
|                              scrollTop: self.terminal.attr("scrollHeight") |  | ||||||
|                          }); |  | ||||||
|                      } |  | ||||||
|                  }); |  | ||||||
|                  $('body').data('tilda', this); |  | ||||||
|                  this.hide(); |  | ||||||
|                  return self; |  | ||||||
|              }; |  | ||||||
|          })(jQuery); |  | ||||||
|  |  | ||||||
|          String.prototype.strip = function(char) { |  | ||||||
|              return this.replace(new RegExp("^\\s*"), '') |  | ||||||
|                         .replace(new RegExp("\\s*$"), ''); |  | ||||||
|          } |  | ||||||
|  |  | ||||||
|          jQuery(document).ready(function($) { |  | ||||||
|              $('#tilda').tilda(function(command, terminal) { |  | ||||||
|                  command = command.strip(); |  | ||||||
|  |  | ||||||
|                  switch (command) { |  | ||||||
|                      case 'help': |  | ||||||
|                          terminal.echo('about  - Go to the about page'); |  | ||||||
|                          terminal.echo(' '); |  | ||||||
|                          terminal.echo('More commands will follow soon!'); |  | ||||||
|  |  | ||||||
|                          break; |  | ||||||
|                      case 'about': |  | ||||||
|                          location = '{{ site_url }}/about/'; |  | ||||||
|  |  | ||||||
|                          break; |  | ||||||
|                      default: |  | ||||||
|                          terminal.echo(command + ': command not found'); |  | ||||||
|  |  | ||||||
|                          break; |  | ||||||
|                  } |  | ||||||
|              }); |  | ||||||
|          }); |  | ||||||
|         </script> |  | ||||||
|         <div id="tilda"></div> |  | ||||||
|     </body> |  | ||||||
| </html> |  | ||||||
| @@ -1,15 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: default |  | ||||||
| --- |  | ||||||
| <div class="post"> |  | ||||||
|  |  | ||||||
|     <header class="post-header"> |  | ||||||
|         <h2>{{page.title}}</h2> |  | ||||||
|         <div class="clearfix"></div> |  | ||||||
|   </header> |  | ||||||
|  |  | ||||||
|   <article class="post-content"> |  | ||||||
|   {{content}} |  | ||||||
|   </article> |  | ||||||
|  |  | ||||||
| </div> |  | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: default |  | ||||||
| render_post: true |  | ||||||
| --- |  | ||||||
| {% assign post = page %} |  | ||||||
| {% include blog-post.html %} |  | ||||||
| <div class="g-plus" data-action="share" data-height="15"></div> |  | ||||||
|  |  | ||||||
| <nav> |  | ||||||
|     <ul class="pager"> |  | ||||||
| {% if page.previous %} |  | ||||||
|         <li class="previous"><a href="{{page.previous.url | prepend: site.baseurl}}">← {{page.previous.title}}</a></li> |  | ||||||
| {% endif %} |  | ||||||
| {% if page.next %} |  | ||||||
|         <li class="next"><a href="{{page.next.url | prepend: site.baseurl}}">{{page.next.title}} →</a></li> |  | ||||||
| {% endif %} |  | ||||||
|     </ul> |  | ||||||
| </nav> |  | ||||||
|  |  | ||||||
| {% include disqus.html %} |  | ||||||
| @@ -1,15 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: default |  | ||||||
| post_listing: true |  | ||||||
| --- |  | ||||||
| <h3 class="tag">{{ page.tag }}</h3> |  | ||||||
| {{content}} |  | ||||||
|  |  | ||||||
| <h4>Articles under this tag</h4> |  | ||||||
|  |  | ||||||
| {% if site.tags[page.tag] %} |  | ||||||
| {%     assign posts = site.tags[page.tag] %} |  | ||||||
| {%     include post-list.html %} |  | ||||||
| {% else %} |  | ||||||
| No posts with this tag. |  | ||||||
| {% endif %} |  | ||||||
| @@ -1,43 +0,0 @@ | |||||||
| #! /bin/sh |  | ||||||
| # |  | ||||||
| # Find all tags in all posts under _posts, and generate a file for |  | ||||||
| # each under blog/tag. Also, if a tag page does not contain the tag: |  | ||||||
| # or layout: keywords, the script will include them in the front |  | ||||||
| # matter. |  | ||||||
|  |  | ||||||
| layout="posts-by-tag" |  | ||||||
|  |  | ||||||
| for tag in `grep -h ^tags: _posts/* | sed -re 's/^tags: +\[//' -e 's/\]$//' -e 's/, /\n/g' | sort | uniq` |  | ||||||
| do |  | ||||||
|     tag_file="blog/tag/${tag}.md" |  | ||||||
|     echo -n "[$tag] " |  | ||||||
|  |  | ||||||
|     if [ ! -f $tag_file ] |  | ||||||
|     then |  | ||||||
|         echo "creating ($tag_file)" |  | ||||||
|  |  | ||||||
|         cat <<EOF > $tag_file |  | ||||||
| --- |  | ||||||
| layout: $layout |  | ||||||
| tag:    $tag |  | ||||||
| --- |  | ||||||
| EOF |  | ||||||
|     else |  | ||||||
|         updated=0 |  | ||||||
|         if ! egrep "^tag: +${tag}$" $tag_file 2>&1 > /dev/null; then |  | ||||||
|             echo "adding tag" |  | ||||||
|             sed -i "0,/---/! s/---/tag:    $tag\\n---/" $tag_file |  | ||||||
|             updated=1 |  | ||||||
|         fi |  | ||||||
|  |  | ||||||
|         if ! egrep "^layout: +" $tag_file 2>&1 > /dev/null; then |  | ||||||
|             echo "adding layout" |  | ||||||
|             sed -i "0,/---/! s/---/layout: $layout\\n---/" $tag_file |  | ||||||
|             updated=1 |  | ||||||
|         fi |  | ||||||
|  |  | ||||||
|         if [ $updated = 0 ]; then |  | ||||||
|             echo "" |  | ||||||
|         fi |  | ||||||
|     fi |  | ||||||
| done |  | ||||||
| @@ -1,29 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Ethical Hacking 2012" |  | ||||||
| date:      2011-05-12 20:54:42 |  | ||||||
| tags:      [conference] |  | ||||||
| permalink: /blog/2011/5/12/ethical-hacking-2011 |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Today I went to the Ethical Hacking conference with my boss. It was my first |  | ||||||
| appearance at such conferences, but I hope there will be more. Although we |  | ||||||
| just started to redesign our IT security infrastructure with a 90% clear goal, |  | ||||||
| it was nice to hear that everything is vulnerable. I was thinking if we should |  | ||||||
| sell all our IT equipments, fire all our colleagues (you know, to prevent |  | ||||||
| social engineering), and move to the South Americas to herd llamas or sheep, |  | ||||||
| so the only danger would be some lurking pumas or jaguars. Or I simply leave |  | ||||||
| my old background image on my desktop, from the well-known game, which says: |  | ||||||
| Trust is a weakness. |  | ||||||
|  |  | ||||||
| Anyways, the conference was really nice. We heard about the weaknesses of |  | ||||||
| Android, Oracle, and even FireWire. They showed some demos about everything, |  | ||||||
| exploited some free and commercial software with no problem at all. We have |  | ||||||
| seen how much power the virtualisation admin has (although I think it can be |  | ||||||
| prevented, but I’m not sure yet). However, in the end, we could see that the |  | ||||||
| Cloud is secure (or at least it can be, in a few months or so), so I’m not |  | ||||||
| totally pessimistic. See you next time at Hacktivity! |  | ||||||
| @@ -1,88 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Gentoo hardened desktop with GNOME 3 – Round one" |  | ||||||
| date:      2011-05-12 20:32:41 |  | ||||||
| tags:      [gentoo, gnome3, selinux] |  | ||||||
| permalink: /blog/2011/5/12/gentoo-hardened-desktop-with-gnome-3-round-one |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| After having some hard times with Ubuntu (upgrading from 10.10 to 11.04), I |  | ||||||
| decided to switch back to my old friend, Gentoo. As I’m currently learning |  | ||||||
| about Linux hardening, I decided to use the new SELinux profile, which |  | ||||||
| supports the v2 reference policy. |  | ||||||
|  |  | ||||||
| Installation was pretty easy, using the [Gentoo x86 |  | ||||||
| Handbook](http://www.gentoo.org/doc/hu/handbook/handbook-x86.xml). This profile |  | ||||||
| automatically turns on the `USE=selinux` flag (so does the old SELinux |  | ||||||
| profile), but deprecated `FEATURE=loadpolicy` (which is turned on by the |  | ||||||
| profile, so portage will complain about it until you disable it in |  | ||||||
| `/etc/make.conf`). |  | ||||||
|  |  | ||||||
| For the kernel, I chose `hardened-sources-2.6.37-r7`. This seems to be recent |  | ||||||
| enough for my security testing needs. I turned on both SELinux, PaX and |  | ||||||
| grsecurity. So far, I have no problem with it, but I don’t have X installed |  | ||||||
| yet, which will screw up things for sure. |  | ||||||
|  |  | ||||||
| After having those hard times with Ubuntu mentioned before, I decided not to |  | ||||||
| install Grub2 yet, as it renders things unusable (eg. my Windows 7 |  | ||||||
| installation, which I sometimes need at the office). So I installed Grub 0.97 |  | ||||||
| (this is the only version marked as stable, as I remember), touched |  | ||||||
| `/.autorelabel`, and reboot. |  | ||||||
|  |  | ||||||
| My first mistake was using an UUID as the root device on the kernel parameter |  | ||||||
| list (I don’t want to list all the small mistakes like forgetting to include to |  | ||||||
| correct SATA driver from my kernel and such). Maybe I was lame, but after |  | ||||||
| including `/dev/sda5` instead of the UUID thing, it worked like… |  | ||||||
|  |  | ||||||
| Well, charm would not be the good word. For example, I forgot to install the |  | ||||||
| lvm2 package, so nothing was mounted except my root partition. After I |  | ||||||
| installed it with the install CD, I assumed everything will be all right, but |  | ||||||
| I was wrong. |  | ||||||
|  |  | ||||||
| udev and LVM is a critical point in a hardened environment. udev itself |  | ||||||
| doesn’t want to work without the `CONFIG_DEVFS_TEMPFS=y` kernel option, so I |  | ||||||
| also had to change that. It seemed that it can be done without the install CD, |  | ||||||
| as it compiled the kernel with no problems. However, when it reached the point |  | ||||||
| when it compresses the kernel with gzip, it stopped with a `Permission denied` |  | ||||||
| message (although it was running with root privileges). |  | ||||||
|  |  | ||||||
| The most beautiful thing in the hardened environment with Mandatory Access |  | ||||||
| Control enabled) is that root is not a real power user any more by default. |  | ||||||
| You can get this kind of messages many times. There are many tools to debug |  | ||||||
| these, I will talk about these later. |  | ||||||
|  |  | ||||||
| So, my gzip needed a fix. After digging a bit on the Internet, I found that |  | ||||||
| the guilty thing is text relocation, which can be corrected if gzip is |  | ||||||
| compiled with PIC enabled. Thus, I turned on `USE=pic` flag globally, and |  | ||||||
| tried to remerge gzip. Of course it failed, as it had to use gzip to unpack |  | ||||||
| the gzip sources. So it did when I tried to install the PaX tools and gradm to |  | ||||||
| turn these checks off. The install CD came to the rescue again, with which I |  | ||||||
| successfully recompiled gzip, and with this new gzip, I compressed my new |  | ||||||
| kernel, with which udev started successfully. So far, so good, let’s try to |  | ||||||
| reboot! |  | ||||||
|  |  | ||||||
| Damn, LVM is still not working. So I decided to finally consult the Gentoo |  | ||||||
| hardened guide. It says that the LVM startup scripts under `/lib/rcscripts/…` |  | ||||||
| must be modified, so LVM will put its lock files under `/etc/lvm/lock` instead |  | ||||||
| of `/dev/.lvm`. After this step and a reboot, LVM worked fine (finally). |  | ||||||
|  |  | ||||||
| The next thing was the file system labelling. SELinux should automatically |  | ||||||
| relabel the entire file system at boot time whenever it finds the |  | ||||||
| `/.autorelabel` file. Well, in my case it didn’t happen. After checking the |  | ||||||
| [Gentoo Hardening](http://wiki.gentoo.org/wiki/Hardened_Gentoo) docs, I realised that the `rlpkg` program does exactly the same |  | ||||||
| (as far as I know, it is designed specifically for Gentoo). So I ran `rlpkg`, |  | ||||||
| and was kind of shocked. It says it will relabel ext2, ext3, xfs and JFS |  | ||||||
| partitions. Oh great, no ext4 support? Well, consulting the forums and adding |  | ||||||
| some extra lines to `/etc/portage/package.keywords` solved the problem (`rlpkg` |  | ||||||
| and some dependencies had to have the `~x86` keyword set). Thus, `rlpkg` |  | ||||||
| relabelled my file systems (I checked some directories with `ls -lZ`, it seemed |  | ||||||
| good for me). |  | ||||||
|  |  | ||||||
| Now it seems that everything is working fine, except the tons of audit |  | ||||||
| messages. Tomorrow I will check them with `audit2why` or `audit2allow` to see if |  | ||||||
| it is related with my SELinux lameness, or with a bug in the policy included |  | ||||||
| with Gentoo. |  | ||||||
| @@ -1,35 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Zabbix performance tip" |  | ||||||
| date:      2011-05-13 19:03:31 |  | ||||||
| tags:      [zabbix, monitoring] |  | ||||||
| permalink: /blog/2011/5/13/zabbix-performance-tip |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Recently I have switched from [MRTG](http://oss.oetiker.ch/mrtg/) + [Cacti](http://www.cacti.net/) + [Nagios](http://www.nagios.org/) + [Gnokii](http://www.gnokii.org/) to [Zabbix](http://www.zabbix.com/), and I |  | ||||||
| must say I’m more than satisfied with it. It can do anything the former tools |  | ||||||
| did, and much more. First of all, it can do the same monitoring as Nagios did, |  | ||||||
| but it does much more fine. It can check several parameters within one |  | ||||||
| request, so network traffic is kept down. Also, its web front-end can generate |  | ||||||
| any kinds of graphs from the collected data, which took Cacti away. Also, it |  | ||||||
| can do SNMP queries (v1-v3), so querying my switches’ port states and traffic |  | ||||||
| made easy, taking MRTG out of the picture (I know Cacti can do it either, it |  | ||||||
| had historical reasons we had both tools installed). And the best part: it can |  | ||||||
| send SMS messages via a GSM modem natively, while Nagios had to use Gnokii. |  | ||||||
| The trade-off is, I had to install Zabbix agent on all my monitored machines, |  | ||||||
| but I think it worths the price. I even have had to install NRPE to monitor |  | ||||||
| some parameters, which can be a pain on Windows hosts, while Zabbix natively |  | ||||||
| supports Windows, Linux and Mac OS/X. |  | ||||||
|  |  | ||||||
| So I only had to create a MySQL database (which I already had for NOD32 |  | ||||||
| central management), and install Zabbix server. Everything went fine, until I |  | ||||||
| reached about 1300 monitored parameters. MySQL seemed to be a bit slow on disk |  | ||||||
| writes, so my Zabbix “queue” filled up in no time. After reading some forums, |  | ||||||
| I decided to switch to PostgreSQL instead. Now it works like charm, even with |  | ||||||
| the default Debian settings. However, I will have to add several more |  | ||||||
| parameters, and my boss wants as many graphs as you can imagine, so I’m more |  | ||||||
| than sure that I will have to fine tune my database later. |  | ||||||
| @@ -1,29 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Gentoo hardened desktop with GNOME 3 – Round two" |  | ||||||
| date:      2011-05-18 10:28:14 |  | ||||||
| tags:      [gentoo, gnome3, selinux] |  | ||||||
| permalink: /blog/2011/5/18/gentoo-hardened-desktop-with-gnome-3-round-two |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| After several hours of `package.keywords`/`package.use` editing and package |  | ||||||
| compiling, I managed to install GNOME 3 on my notebook. Well, I mean, the |  | ||||||
| GNOME 3 packages. Unfortunately the fglrx driver didn’t seem to recognise my |  | ||||||
| ATI Mobility M56P card, and the open source driver didn’t want to give me GLX |  | ||||||
| support. When I finally found some clues on what should I do, I had to use my |  | ||||||
| notebook for work, so I installed Fedora 14 on it. Then I realised that GNOME |  | ||||||
| 3 is already included in Rawhide (Fedora 15), so I quickly downloaded and |  | ||||||
| installed that instead. Now I have to keep this machine in a working state for |  | ||||||
| a few days, so I will learn SELinux stuff in its native environment. |  | ||||||
|  |  | ||||||
| When I installed Fedora 14, the first AVC message popped up after about ten |  | ||||||
| minutes. That was a good thing, as I wanted to see `setroubleshoot` in action. |  | ||||||
| However, in Fedora 15, the AVC bubbles didn’t show up even after a day. I |  | ||||||
| raised my left eyebrow and said that’s impossible, SELinux must be disabled. |  | ||||||
| And it’s not! It’s even in enforcing mode! And it works just fine. I like it, |  | ||||||
| and I hope I will be able to get the same results with Gentoo if I can get |  | ||||||
| back to testing… |  | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Citrix XenServer 5.5 vs. Debian 5.0 upgrade to 6.0" |  | ||||||
| date:      2011-05-27 17:33:41 |  | ||||||
| tags:      [citrix-xenserver, debian] |  | ||||||
| permalink: /blog/2011/5/27/citrix-xenserver-vs-debian-5-0-upgrade-to-6-0 |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Few weeks ago I’ve upgraded two of our Debian based application servers from |  | ||||||
| 5.0 to 6.0. Everything went fine, as the upgraded packages worked well with |  | ||||||
| the 4.2 JBoss instances. For the new kernel we needed a reboot, but as the |  | ||||||
| network had to be rebuilt, I postponed this reboot until the network changes. |  | ||||||
| With the network, everything went fine again, we successfully migrated our |  | ||||||
| mail servers behind a firewall. Also the Xen server (5.5.0, upgrade to 5.6 |  | ||||||
| still has to wait for a week or so) revolted well with some storage disks |  | ||||||
| added. But the application servers remained silent… |  | ||||||
|  |  | ||||||
| After checking the console, I realised that they don’t have an active console. |  | ||||||
| And when I tried to manually start them, XenServer refused with a message |  | ||||||
| regarding pygrub. |  | ||||||
|  |  | ||||||
| To understand the problem, I had to understand how XenServer boots Debian. It |  | ||||||
| reads the grub.conf on the first partition’s root or `/boot` directory, and |  | ||||||
| starts the first option, without asking (correct me, if I’m mistaken |  | ||||||
| somewhere). However, this pygrub thing can not parse the new, grub2 config. |  | ||||||
| This is kinda frustrating. |  | ||||||
|  |  | ||||||
| For the first step, I quickly installed a new Debian 5.0 system from my |  | ||||||
| template. Then I attached the disks of the faulty virtual machine, and mounted |  | ||||||
| all its partitions. This way I could reach my faulty 6.0 system with a chroot |  | ||||||
| shell, from which I could install the `grub-legacy` package instead of grub, |  | ||||||
| install the necessary kernel and XenServer tools (which were missing from both |  | ||||||
| machines somehow), then halt the rescue system, and start up the original |  | ||||||
| instance. |  | ||||||
|  |  | ||||||
| Next week I will do an upgrade on the XenServer to 5.6.1. I hope no such |  | ||||||
| problems will occur. |  | ||||||
| @@ -1,25 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Oracle Database “incompatible” with Oracle Linux?" |  | ||||||
| date:      2011-05-27 17:53:31 |  | ||||||
| tags:      [linux, oracle] |  | ||||||
| permalink: /blog/2011/5/27/oracle-database-incompatible-with-oracle-linux |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Today I gave a shot to install [Oracle |  | ||||||
| Linux](http://www.oracle.com/us/technologies/linux/overview/index.html). I thought I could easily install |  | ||||||
| an Oracle DBA on it. Well, I was naive. |  | ||||||
|  |  | ||||||
| As only the 5.2 version is supported by XenServer 5.5, I downloaded that |  | ||||||
| version of Oracle Linux. Installing it was surprisingly fast and easy, it |  | ||||||
| asked almost nothing, and booted without any problems. |  | ||||||
|  |  | ||||||
| After this came the DBA, 10.2, which bloated an error message in my face |  | ||||||
| saying that this is an unsupported version of Linux. Bah. |  | ||||||
|  |  | ||||||
| Is it only me, or is it really strange that Oracle doesn’t support their own |  | ||||||
| distro? |  | ||||||
| @@ -1,22 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Proxy only non-existing files with mod_proxy and mod_rewrite" |  | ||||||
| date:      2011-06-10 14:20:43 |  | ||||||
| tags:      [apache] |  | ||||||
| permalink: /blog/2011/6/10/proxy-only-non-existing-files-with-mod-proxy-and-mod-rewrite |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Today I got an interesting task. I had to upload some pdf documents to a site. |  | ||||||
| The domain is ours, but we don’t have access to the application server that is |  | ||||||
| hosting the page yet. Until we get it in our hands, I did a trick. |  | ||||||
|  |  | ||||||
| I enabled `mod_rewrite`, `mod_proxy` and `mod_proxy_http`, then added the following |  | ||||||
| lines to my apache config: |  | ||||||
|  |  | ||||||
| {% gist 47680bfa44eb29708f20 redirect-non-existing.conf %} |  | ||||||
|  |  | ||||||
| I’m not totally sure it’s actually secure, but it works for now. |  | ||||||
| @@ -1,30 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Inverse of `sort`" |  | ||||||
| date:      2011-09-18 14:57:31 |  | ||||||
| tags:      [linux, command-line] |  | ||||||
| permalink: /blog/2011/9/18/inverse-of-sort |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I’m using \*NIX systems for about 14 years now, but it can still show me new |  | ||||||
| things. Today I had to generate a bunch of random names. I’ve create a small |  | ||||||
| perl script which generates permutations of some usual Hungarian first and |  | ||||||
| last names, occasionally prefixing it with a ‘Dr.’ title or using double first |  | ||||||
| names. For some reasons I forgot to include uniqueness check in the script. |  | ||||||
| When I ran it in the command line, I realized the mistake, so I appended |  | ||||||
| `| sort | uniq` to the command line. So I had around 200 unique names, but in |  | ||||||
| alphabetical order, which was awful for my final goal. Thus, I tried shell |  | ||||||
| commands like rand to create a random order, and when many of my tries failed, |  | ||||||
| the idea popped in my mind (not being a native English speaker): “I don’t have |  | ||||||
| to create «random order», but «shuffle the list». So I started typing `shu`, |  | ||||||
| pressed Tab in the Bash shell, and voilà! `shuf` is the winner, it does just |  | ||||||
| exactly what I need: |  | ||||||
|  |  | ||||||
|     **NAME** |  | ||||||
|       shuf - generate random permutations |  | ||||||
|  |  | ||||||
| Thank you, Linux Core Utils! :) |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Why you should always test your software with production data" |  | ||||||
| date:      2011-12-11 12:14:51 |  | ||||||
| tags:      [development, testing, ranting] |  | ||||||
| permalink: /blog/2011/12/11/why-you-should-always-test-your-software-with-production-data |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I’m writing a software for my company in PHP, using the Symfony 2 framework. |  | ||||||
| I’ve finished all the work, created some sample data, it loaded perfectly. Now |  | ||||||
| I put the whole thing into production and tried to upload the production data |  | ||||||
| into it. Guess what… it didn’t load. |  | ||||||
| @@ -1,29 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "PHP 5.4 released" |  | ||||||
| date:      2012-03-20 13:31:12 |  | ||||||
| tags:      [php] |  | ||||||
| permalink: /blog/2012/3/20/php-5-4-released |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| After a long time of waiting, PHP announced 5.4 release on 1 March (also, |  | ||||||
| today they announced that they finally migrate to Git, which is sweet from my |  | ||||||
| point of view, but it doesn’t really matter). |  | ||||||
|  |  | ||||||
| About a year ago we became very agressive towards a developer who created our |  | ||||||
| internal e-learning system. Their database was very insecure, and they didn’t |  | ||||||
| really follow industry standards in many ways. Thus, we forced them to move |  | ||||||
| from Windows + Apache 2.0 + PHP 5.2 + MySQL 4.0 to Debian Linux 6.0 + Apache |  | ||||||
| 2.2 + PHP 5.3 + MySQL 5.1. It was fun (well, from our point of view), as their |  | ||||||
| coders… well… they are not so good. The code that ran “smoothly” on the |  | ||||||
| old system failed at many points on the new one. So they code and code, and |  | ||||||
| write more code. And they still didn’t finish. And now 5.4 is here. Okay, I |  | ||||||
| know it will take some time to get into the Debian repositories, but it’s |  | ||||||
| here. And they removed `register_globals`, which will kill that funny code again |  | ||||||
| at so many points that they will soon get to rewrite the whole code to make it |  | ||||||
| work. And I just sit here in my so-much-comfortable chair, and laugh. Am I |  | ||||||
| evil? |  | ||||||
| @@ -1,34 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Fast world, fast updates" |  | ||||||
| date:      2012-03-27 06:18:43 |  | ||||||
| tags:      [linux] |  | ||||||
| permalink: /blog/2012/3/27/fast-world-fast-updates |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| We live in a fast world, that’s for sure. When I first heard about Ubuntu |  | ||||||
| Linux and their goals, I was happy: they gave a Debian to everyone, but in |  | ||||||
| different clothes. It had fresh software in it, and even they gave support of |  | ||||||
| a kind. It was easy to install and use, even if one had no Linux experience |  | ||||||
| before. So people liked it. I’ve even installed it on some of my servers |  | ||||||
| because of the new package versions that came more often. Thus I got an up to |  | ||||||
| date system. However, it had a price. After a while, security updates came |  | ||||||
| more and more often, and when I had a new critical update every two or three |  | ||||||
| days, I’ve decided to move back to Debian. Fortunately I did this at the time |  | ||||||
| of a new release, so I didn’t really loose any features. |  | ||||||
|  |  | ||||||
| After a few years passed, even Debian is heading this very same way. But as I |  | ||||||
| see, the cause is not the same. It seems that upstream software is hitting |  | ||||||
| these bugs, and even the Debian guys don’t have the time to check for them. At |  | ||||||
| the time of a GNOME version bump (yes, GNOME 3 is a really big one for the |  | ||||||
| UN\*X-like OSes), when hundreds of packages need to be checked, security bugs |  | ||||||
| show off more often. On the other hand however, Debian is releasing a new |  | ||||||
| security update every day (I had one on each of the last three days). This, of |  | ||||||
| course, is good from one point of view as we get a system that is more secure, |  | ||||||
| but most administrators don’t have maintenance windows this often. I can think |  | ||||||
| of some alternatives like Fedora, but do I really have to change? Dear fellow |  | ||||||
| developers, please code more carefully instead! |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Wordpress madness" |  | ||||||
| date:      2012-06-14 06:40:12 |  | ||||||
| tags:      [wordpress, ranting] |  | ||||||
| permalink: /blog/2012/6/14/wordpress-madness |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I’m a bit fed up that I had to install [MySQL](http://www.mysql.com/) on my |  | ||||||
| server to have [Wordpress](http://wordpress.org/) working, so I’ve Googled a |  | ||||||
| bit to find a solution for my pain. I found |  | ||||||
| [this](http://codex.wordpress.org/Using_Alternative_Databases). I don’t know when |  | ||||||
| this post was written, but I think it’s a bit out of date. I mean come on, PDO |  | ||||||
| is the part of PHP for ages now, and they say adding a DBAL to the dependencies |  | ||||||
| would be a project as large as (or larger than) WP itself. Well, |  | ||||||
| yes, but PHP is already a dependency, isn’t it? Remove it guys, it’s too |  | ||||||
| large! |  | ||||||
|  |  | ||||||
| Okay, to be serious… Having a heavily MySQL dependent codebase is a bad |  | ||||||
| thing in my opinion, and changing it is no easy task. But once it is done, it |  | ||||||
| would be a child’s play to keep it up to date, and to port WP to other |  | ||||||
| database backends. And it would be more than enough to call it 4.0, and |  | ||||||
| raising version numbers fast is a must nowadays (right, Firefox and Linux |  | ||||||
| Kernel guys?) |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "SSH login FAILed on Red Had Enterprise Linux 6.2" |  | ||||||
| date:      2012-06-18 18:28:45 |  | ||||||
| tags:      [linux, selinux, ssh, red-hat] |  | ||||||
| permalink: /blog/2012/6/18/ssh-login-failed-on-red-hat-enterprise-linux-6-2 |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Now this was a mistake I should not have done… |  | ||||||
|  |  | ||||||
| About a month ago I have moved my AWS EC2 machine from Amazon Linux to RHEL |  | ||||||
| 6.2. This was good. I have moved all my files and stuff, recreated my own |  | ||||||
| user, everything was just fine. Then I copied my |  | ||||||
| [gitosis](https://github.com/tv42/gitosis) account (user `git` and its home |  | ||||||
| directory). Then I tried to log in. It failed. I was blaming OpenSSH for a week |  | ||||||
| or so, changed the config file in several ways, tried to change the permissions |  | ||||||
| on `~git/.ssh/*`, but still nothing. Permission were denied, I was unable to |  | ||||||
| push any of my development changes. Now after a long time of trying, I |  | ||||||
| coincidently `tail -f`-ed `/var/log/audit/audit.log` (wanted to open `auth.log` |  | ||||||
| instead) and that was my first good point. It told me that `sshd` was unable to |  | ||||||
| read `~git/.ssh/authorized_keys`, which gave me the idea to run `restorecon` on |  | ||||||
| `/home/git`. It solved the problem. |  | ||||||
|  |  | ||||||
| All hail SELinux and RBAC! |  | ||||||
| @@ -1,35 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Upgrades requiring a reboot on Linux? At last!" |  | ||||||
| date:      2012-06-22 20:04:51 |  | ||||||
| tags:      [linux] |  | ||||||
| permalink: /blog/2012/6/22/upgrades-requiring-a-reboot-on-linux-at-last |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I’ve recently received an article on Google+ about Fedora’s new idea: package |  | ||||||
| upgrades that require a reboot. The article said that Linux guys have lost |  | ||||||
| their primary adoo: “Haha! I don’t have to reboot my system to install system |  | ||||||
| upgrades!” My answer was always this: “Well, actually you should…” |  | ||||||
|  |  | ||||||
| I think this can be a great idea if distros implement it well. PackageKit was |  | ||||||
| a good first step on this road. That software could easily solve such an |  | ||||||
| issue. However, it is sooo easy to do it wrong. The kernel, of course, can not |  | ||||||
| be upgraded online (or could it be? I have some theories on this subject, |  | ||||||
| wonder if it can be implemented…), but other packages are much different. |  | ||||||
| From the users’ point of view the best would be if the packages would be |  | ||||||
| upgraded in the background seemlessly. E.g. PackageKit should check if the |  | ||||||
| given executable is running. If not, it should upgrade it, while notifying the |  | ||||||
| user like “Hey dude, don’t start Anjuta now, I’m upgrading it!”, or simply |  | ||||||
| denying to start it. Libraries are a bit different, as PackageKit should check |  | ||||||
| if any running executables are using the library. Meanwhile, PK should also |  | ||||||
| keep a notification somewhere telling the users that some packages could be |  | ||||||
| upgraded, but without stopping this-and-that, it can not be done. |  | ||||||
|  |  | ||||||
| I know these things are easier said than done. But I think (a) users should |  | ||||||
| tell such ideas to the developers and (b) developers (mostly large companies, |  | ||||||
| like Microsoft or Apple) should listen to them, and at least think of these |  | ||||||
| ideas. Some users are not as stupid as they think… |  | ||||||
| @@ -1,80 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Some thoughts about that dead Linux Desktop" |  | ||||||
| date:      2012-09-05 09:01:31 |  | ||||||
| tags:      [linux] |  | ||||||
| permalink: /blog/2012/9/5/some-thoughts-about-that-dead-linux-desktop |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| There were some arguments in the near past on [What Killed the Linux |  | ||||||
| Desktop](http://tirania.org/blog/archive/2012/Aug-29.html). After reading many |  | ||||||
| replies, like [Linus |  | ||||||
| Torvalds’](http://www.zdnet.com/linus-torvalds-on-the-linux-desktops-popularity-problems-7000003641/), |  | ||||||
| I have my own thoughts, too. |  | ||||||
|  |  | ||||||
| I know my place in the world, especially in the online community. I’m a Linux |  | ||||||
| user for about 15 years and a Linux administrator for 10 years now, beginning |  | ||||||
| with WindowMaker and something that I remember as GNOME without a version |  | ||||||
| number. I have committed some minor code chunks and translations in some minor |  | ||||||
| projects, so I’m not really into it from the “write” side (well, until now, |  | ||||||
| since I have began to write this blog, and much more, but don’t give a penny |  | ||||||
| for my words until you see it). |  | ||||||
|  |  | ||||||
| I’m using Linux since 2.2 and GNOME since 1.whatever. It’s nice that a program |  | ||||||
| compiled years ago still runs on today’s Linux kernel, especially if you see |  | ||||||
| old DOS/Windows software failing to start on a new Windows 7 machine. I |  | ||||||
| understand Linus’ point that breaking external APIs is bad, and I think it can |  | ||||||
| work well on the kernel’s level. But the desktop level is much different. As |  | ||||||
| the Linux Desktop has such competitors (like OS/X and Windows’ Aero and Metro), |  | ||||||
| they have to give something new to the users almost every year to keep up with |  | ||||||
| them. Eye candies are a must (yes, of course my techy fellows, they are |  | ||||||
| worthless, but users *need* it), and they can not be created without extending |  | ||||||
| APIs. And the old API… well, it fades away fast. I don’t really understand |  | ||||||
| however, why they have to totally disappear, like |  | ||||||
| [GTK_DIALOG_NO_SEPARATOR](http://developer.gnome.org/gtk/stable/GtkDialog.html#GtkDialogFlags) |  | ||||||
| in Gtk3. It could be replaced with a 0 value (e.g: it won’t do anything). This |  | ||||||
| way my old Gtk2 program could compile with Gtk3 nicely. Also, there could be a |  | ||||||
| small software that goes through your source code and warn you about such |  | ||||||
| deprecated (and no-doer but still working) things. Porting applications between |  | ||||||
| Gtk (and thus, GNOME) versions became a real pain, which makes less enthusiast |  | ||||||
| programmers stop developing for Linux. Since I’m a GNOME guy for years, I can |  | ||||||
| tell nothing about Qt and KDE, but for the GNOME guys, this is a bad thing. As |  | ||||||
| of alternatives, there is Java. No, wait… it turned out recently that [it has |  | ||||||
| several security |  | ||||||
| bugs](http://www.theregister.co.uk/2012/08/31/critical_flaw_found_in_patched_java). |  | ||||||
| Also it’s not that multiplatform as they say (I can’t find the article on |  | ||||||
| that at the moment, but I have proof). Also, the JVMs out there eat up so much |  | ||||||
| resources, which makes it a bit hard and expensive to use. |  | ||||||
|  |  | ||||||
| Also, I see another problem: those blasted package managers. RPM, DPKG, |  | ||||||
| Portage, whatever. What the hell? Why are there so many? Why do developers |  | ||||||
| reinvent the wheel? The nave is too small or there are to few spokes? Come on… |  | ||||||
| we live in an open source world! Contribute to the one and only package manager |  | ||||||
| (which one is that I don’t actually care)! I’m sure the two (three, many) |  | ||||||
| bunches of develoeprs could make a deal. Thus, it could become better and |  | ||||||
| “outsider” companies would be happier to distribute their software for Linux |  | ||||||
| platforms. |  | ||||||
|  |  | ||||||
| And now that we get to the big companies. I don’t really understand them. |  | ||||||
| nVidia and ATI made their own closed source drivers for Linux. Some other |  | ||||||
| hardware vendors also write Linux drivers, and as the kernel API doesn’t really |  | ||||||
| change, they will work for a long time. But what about desktop |  | ||||||
| application vendors? Well, they try to stick to a desktop environment or two, |  | ||||||
| and if they change too frequently, they stop developing for Linux, like Skype |  | ||||||
| did (OK, maybe Skype has other reasons, but you see my point). But why? The |  | ||||||
| main part for Linux programs is the Linux kernel and the basic userland like |  | ||||||
| libc/stdlib++. If you write graphical software, it will have to use X-Windows. |  | ||||||
| Yes, it’s much different in many ways, mostly because they have a… well… pretty |  | ||||||
| ugly design by default. But still, it’s the same on every Linux distributions, |  | ||||||
| as it became somewhat an industry standard, as it was already on the market |  | ||||||
| back in the old UN\*X days. The protocol itself changed just like the Linux |  | ||||||
| kernel: almost no change at all, just some new features. |  | ||||||
|  |  | ||||||
| So what kills the Linux desktop in my opinion is these constant wars inside, |  | ||||||
| and the lack of support from the outside. Open Source is good, but until these |  | ||||||
| (mostly the first) problems are not resolved, Linux Desktop can do nothing on |  | ||||||
| the market. It’s a downward spiral hard to escape. |  | ||||||
| @@ -1,76 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "How to start becoming a web developer" |  | ||||||
| date:      2012-09-07 18:12:12 |  | ||||||
| tags:      [development, technology] |  | ||||||
| permalink: /blog/2012/9/7/how-to-start-becoming-a-web-developer |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| A friend of mine asked me today how to become a web developer. It took me a |  | ||||||
| while, but I made up a checklist. It’s short, but it’s enough for the first |  | ||||||
| steps. |  | ||||||
|  |  | ||||||
| ####  First of all, learn English |  | ||||||
|  |  | ||||||
| Well, if you read this, maybe this was a bad first point… |  | ||||||
|  |  | ||||||
| ####  Choose a language and stick to it! |  | ||||||
|  |  | ||||||
| For the UN\*X/Linux line, there is PHP. It’s free, easy to learn, and has many |  | ||||||
| free tools and documentations available. It can be used in a functional or an |  | ||||||
| object-oriented way. |  | ||||||
|  |  | ||||||
| C# is another good way to start, but for the Windows line. It’s fully object- |  | ||||||
| oriented, and the web is full of tutorials, how-tos and other resources. |  | ||||||
|  |  | ||||||
| ####  Learn the basics of the system you are working on |  | ||||||
|  |  | ||||||
| To become a good developer, learn at least the basics of the system you are |  | ||||||
| working on. Basic commands can always come in handy. Debugging (yes, you will |  | ||||||
| do tons of bugs for sure) can become much easier if you know the huge set of |  | ||||||
| tools provided by your OS. You should also try to develop in the chosen |  | ||||||
| environment. Chose PHP? Get a Linux desktop! ASP.NET? Get a Windows. |  | ||||||
| Everything will be much easier! |  | ||||||
|  |  | ||||||
| ####  Learn the basics of the web server you are using |  | ||||||
|  |  | ||||||
| PHP can run on [Apache](http://httpd.apache.org/) (as a module), or any |  | ||||||
| CGI-capable webserver, like [lighttpd](http://www.lighttpd.net/) or |  | ||||||
| [nginx](http://nginx.org/) (well, it can also run on IIS, but trust me: you |  | ||||||
| don’t want that). ASP.NET is designed for IIS, and although some scripts can |  | ||||||
| be run under a mono-capable server, it should still stay there. |  | ||||||
|  |  | ||||||
| Whichever you choose, learn the basics! How to start and stop the service, |  | ||||||
| basic configuration methods, modules/extensions, and so on. It’s more than sure |  | ||||||
| that you will face some issues while developing, so it can never hurt. |  | ||||||
|  |  | ||||||
| ####  Keep your versions under control |  | ||||||
|  |  | ||||||
| Version control is critical nowadays. It gives you a basic backup solution, |  | ||||||
| can come in handy with debugging, and if you ever want to work in a team, you |  | ||||||
| will badly need it. |  | ||||||
|  |  | ||||||
| Subversion is a bit out of date now, and it’s kind of hard to set up. |  | ||||||
|  |  | ||||||
| Git is no easy. You will have to learn a lot of stuff, but basicly it’s just |  | ||||||
| another version control system. Just choose if you want to stick to |  | ||||||
| merge-then-commit or rebase-then-commit, get a client, and get on the run. |  | ||||||
|  |  | ||||||
| Microsoft’s Team Foundation is another good way if you are working in a team. |  | ||||||
| It provides several other features besides version controlling, and is well |  | ||||||
| integrated into Visual Studio, which is highly recommended for Windows based |  | ||||||
| development. |  | ||||||
|  |  | ||||||
| ####  Choose an environment to work in |  | ||||||
|  |  | ||||||
| There are so many good tools out there. You should choose according to the |  | ||||||
| language and OS on what you are working on. [Zend |  | ||||||
| Studio](http://www.zend.com/en/products/studio) or |  | ||||||
| [Netbeans](https://netbeans.org/) are both good tools for PHP development, |  | ||||||
| while [Visual Studio](http://www.visualstudio.com/) is a best buy for Windows |  | ||||||
| development. Both of these have many ups and downs, but once you get in touch |  | ||||||
| with their deeper parts, you will like them. |  | ||||||
| @@ -1,19 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Do-Not-Track in IE10 vs. Apache" |  | ||||||
| date:      2012-09-10 20:22:32 |  | ||||||
| tags:      [apache, technology] |  | ||||||
| permalink: /blog/2012/9/10/do-not-track-in-ie10-vs-apache |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| [Apache developer decided not to accept Do-Not-Track headers from IE10 |  | ||||||
| users](http://arstechnica.com/security/2012/09/apache-webserver-updated-to-ignore-do-not-track-settings-in-ie-10/), |  | ||||||
| because it’s enabled by default. So… if I install a plugin that hides the |  | ||||||
| fact from the web server that I’m using IE10, I become eligible of using |  | ||||||
| it. But if I do this, I simply became eligible because I consciously installed |  | ||||||
| that addon, so I could actually use it without hiding the fact. Sorry if |  | ||||||
| I’m a bit Philosoraptorish… |  | ||||||
| @@ -1,64 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Symfony 2 – Create role- and class-based ACLs with your roles coming from the ORM" |  | ||||||
| date:      2012-09-16 18:39:25 |  | ||||||
| tags:      [php, symfony] |  | ||||||
| permalink: /blog/2012/9/16/symfony-2-create-role-and-class-based-acls-with-your-roles-coming-from-the-orm |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| During the last weeks I had some serious issues with one of my private Symfony |  | ||||||
| 2 projects. One of my goals was to create a dynamic security system, e.g my |  | ||||||
| administrators wanted to create roles, and grant these roles access to |  | ||||||
| different object types (classes) and/or objects. |  | ||||||
|  |  | ||||||
| So I have created a `User` entity, which implements `UserInterface` and |  | ||||||
| `AdvancedUserInterface`, the latter for the possibility to enable/disable |  | ||||||
| accounts and such. It had a `$roles` property, which was a `ManyToMany` relation |  | ||||||
| to the `Role` entity, which implemented `RoleInterface`. Also I have created my |  | ||||||
| own role hierarchy service that implements `RoleHierarchyInterface`. |  | ||||||
|  |  | ||||||
| So far so good, first tests. It soon turned out that if `User::getRoles()` |  | ||||||
| returns a `DoctrineCollection` as it does by default, then the standard |  | ||||||
|  |  | ||||||
| {% gist 883ace4f35e440f6fe0f WhatEver.php %} |  | ||||||
|  |  | ||||||
| doesn’t work. I know, it should not be hard coded, as my roles and permission |  | ||||||
| tables are dynamic, I have just tested. So I fixed my `User` entity so |  | ||||||
| `getRoles()` returns an array of `Role` objects instead of the |  | ||||||
| `DoctrineCollection`. Also I implemented a `getRolesCollection()` method to |  | ||||||
| return the original collection, but I think it will never be used. |  | ||||||
|  |  | ||||||
| After that, I had to implement some more features so I put this task away. |  | ||||||
| Then, I tried to create my first ACL. |  | ||||||
|  |  | ||||||
| {% gist 883ace4f35e440f6fe0f WhatEver2.php %} |  | ||||||
|  |  | ||||||
| I was about to check if the user who is logged in has an `OWNER` permission on |  | ||||||
| the `User` class. |  | ||||||
|  |  | ||||||
| {% gist 883ace4f35e440f6fe0f WhatEver3.php %} |  | ||||||
|  |  | ||||||
| The ACL was defined based on a role, so everyone who had the `ROLE_ADMIN` role |  | ||||||
| should gain access to the user listing page. But they didn’t. It took several |  | ||||||
| weeks to find the cause, I have put it on |  | ||||||
| [stackoverflow](http://stackoverflow.com/questions/12057795/symfony-2-1-this-getsecurity-context-isgrantedrole-admin-returns-fa) |  | ||||||
| and the Symfony Google Group, but no usable answers. |  | ||||||
|  |  | ||||||
| Then I went off for debugging. Setting up NetBeans for xdebug-based PHP |  | ||||||
| debugging was real fun under Fedora, but that’s another story. After a while I |  | ||||||
| have found that Symfony’s basic access decision manager checks for |  | ||||||
| `$role->getRole()` only if `$role` is an instance of |  | ||||||
| `Symfony\Component\Security\Core\Role\Role`, instead of checking if the object |  | ||||||
| implements `Symfony\Component\Security\Core\Role\RoleInterface`. So I’ve |  | ||||||
| checked if the bug is already reported. It turned out that it was, and my |  | ||||||
| solution was available in a specific commit about a year ago, but as [Johannes |  | ||||||
| Schmitt commented, it would introduce a security |  | ||||||
| issue](https://github.com/symfony/symfony/commit/af70ac8d777873c49347ac828a817a400006cbea), |  | ||||||
| so it was reverted. Unfortunately neither Johannes Schmitt, nor Fabien |  | ||||||
| Potencier (nor anyone else) could (or wanted) to tell about this issue. So the |  | ||||||
| final (and somewhat hack-like) solution was to extend |  | ||||||
| `Symfony\Component\Security\Core\Role\Role`. And boom! It worked. |  | ||||||
| @@ -1,25 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "SmsGateway and SmsSender" |  | ||||||
| date:      2012-10-07 00:10:26 |  | ||||||
| tags:      [development, php, symfony] |  | ||||||
| permalink: /blog/2012/10/7/smsgateway-and-smssender |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I have just uploaded my SmsGateway, SmsSender and SmsSenderBundle packages to |  | ||||||
| [GitHub](http://github.com/gergelypolonkai) and |  | ||||||
| [Packagist](http://packagist.org). I hope some of you will find it useful. |  | ||||||
|  |  | ||||||
| * SmsGateway |  | ||||||
|   * [GitHub](https://github.com/gergelypolonkai/smsgateway) |  | ||||||
|   * [Packagist](https://packagist.org/packages/gergelypolonkai/smsgateway) |  | ||||||
| * SmsSender |  | ||||||
|   * [GitHub](https://github.com/gergelypolonkai/smssender) |  | ||||||
|   * [Packagist](https://packagist.org/packages/gergelypolonkai/smssender) |  | ||||||
| * SmsSenderBundle |  | ||||||
|   * [GitHub](https://github.com/gergelypolonkai/smssender-bundle) |  | ||||||
|   * [Packagist](https://packagist.org/packages/gergelypolonkai/smssender-bundle) |  | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Changing the session cookie’s name in Symfony 2" |  | ||||||
| date:      2012-10-13 12:49:28 |  | ||||||
| tags:      [symfony, development] |  | ||||||
| permalink: /blog/2012/10/13/changing-the-session-cookie-s-name-in-symfony-2 |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I have a development server, on which I have several Symfony 2.x projects under |  | ||||||
| the same hostname in different directories. Now I’m facing a funny problem |  | ||||||
| which is caused by that the cookies Symfony places for each of my projects have |  | ||||||
| the same name. |  | ||||||
|  |  | ||||||
| To change this, you will have to modify the `config.yml` file like this: |  | ||||||
|  |  | ||||||
| {% gist c695670ecca2809f7c93 %} |  | ||||||
| @@ -1,22 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Symfony 2 Configuration – Array of associative arrays" |  | ||||||
| date:      2012-12-20 12:03:23 |  | ||||||
| tags:      [php, symfony] |  | ||||||
| permalink: /blog/2012/12/20/symfony-2-configuration-array-of-associative-arrays |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Few days ago I have struggled with a problem using Symfony2 configuration. I |  | ||||||
| wanted to add the following kind of configuration to `config.yml`: |  | ||||||
|  |  | ||||||
| {% gist 30440e25f7a447730064 config.yml %} |  | ||||||
|  |  | ||||||
| The problem was that the stuff under `transitions` is dynamic, so those |  | ||||||
| `hc_cba` and `cba_hc` tags can be pretty much anything. After hitting many |  | ||||||
| errors, I came to the solution: |  | ||||||
|  |  | ||||||
| {% gist 30440e25f7a447730064 DynarrayConfiguration.php %} |  | ||||||
| @@ -1,14 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Development man pages on Fedora" |  | ||||||
| date:      2013-01-05 18:20:41 |  | ||||||
| tags:      [development, fedora] |  | ||||||
| permalink: /blog/2013/1/5/development-man-pages-on-fedora |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| If you use Fedora (like me), and can’t find the development manual pages for |  | ||||||
| e.g. `printf(3)` (like me), just `yum install man-pages` (like me). |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Registering an enum type in GLib’s type system" |  | ||||||
| date:      2013-01-06 02:34:03 |  | ||||||
| tags:      [c, development, glib] |  | ||||||
| permalink: /blog/2013/1/6/registering-an-enum-type-in-glib-s-type-system |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I faced a problem in my [GLib](https://developer.gnome.org/glib/) self-teaching |  | ||||||
| project, [wMUD](https://github.com/gergelypolonkai/wmud) today. I wanted to |  | ||||||
| register a signal for a `GObject`, whose handler should accept two `enum` |  | ||||||
| parameters for which I had to register a new `GEnum` type in the `GObject` type |  | ||||||
| system. However, the [documentation on this |  | ||||||
| feature](https://developer.gnome.org/gobject/unstable/gtype-non-instantiable.html) |  | ||||||
| (thanks for pointing out goes to hashem on `#gnome-hackers`) is not… uhm… |  | ||||||
| obvious. Making the long story short, I have checked with the `GIO` sources for |  | ||||||
| an example, and using that, I have created this small, working chunk: |  | ||||||
|  |  | ||||||
| {% gist 47794b6fb94484f8160b client-state.h %} |  | ||||||
|  |  | ||||||
| {% gist 47794b6fb94484f8160b client-state.c %} |  | ||||||
|  |  | ||||||
| Still, it can be made more perfect by using the |  | ||||||
| [glib-mkenums](http://developer.gnome.org/gobject/stable/glib-mkenums.html) |  | ||||||
| tool. I will read through the GLib Makefiles tomorrow for some hints on |  | ||||||
| this. |  | ||||||
|  |  | ||||||
| Edit: you can find the glib-mkenums solution [here]({% post_url 2014-08-16-registering-an-enum-type-in-glib-glib-mkenums-magic %}). |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "git rm --cached madness" |  | ||||||
| date:      2013-01-14 21:38:00 |  | ||||||
| tags:      [development, git] |  | ||||||
| permalink: /blog/2013/1/14/git-rm-cached-madness |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I have recently learned about `git rm --cached`. It’s a very good tool, as it |  | ||||||
| removes a file from tracking, without removing your local copy of it. However, |  | ||||||
| be warned that if you use `git pull` in another working copy, the file will be |  | ||||||
| removed from there! If you accidentally put the configuration of a production |  | ||||||
| project, and remove it on your dev machine, it can cause a lot of trouble ;) |  | ||||||
| @@ -1,52 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "JMS\\DiExtraBundle’s GrepPatternFinder – grep exits with status code 2 on Fedora 18" |  | ||||||
| date:      2013-01-17 00:32:12 |  | ||||||
| tags:      [fedora, selinux, symfony] |  | ||||||
| permalink: /blog/2013/1/17/jms-diextrabundle-s-greppatternfinder-grep-exits-with-status-code-2-on-fedora-18 |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Yesterday I’ve upgraded my development machines from Fedora 17 to Fedora |  | ||||||
| 18. Although it went well, my [Symfony](http://symfony.com/) projects stopped |  | ||||||
| working with a message like this: |  | ||||||
|  |  | ||||||
|     RuntimeException: Command "/usr/bin/grep --fixed-strings --directories=recurse --devices=skip --files-with-matches --with-filename --color=never --include=*.php 'JMS\DiExtraBundle\Annotation' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/app/../src' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/symfony/monolog-bundle/Symfony/Bundle/MonologBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/symfony/assetic-bundle/Symfony/Bundle/AsseticBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/sensio/framework-extra-bundle/Sensio/Bundle/FrameworkExtraBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/jms/aop-bundle/JMS/AopBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/doctrine/doctrine-migrations-bundle/Doctrine/Bundle/MigrationsBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/friendsofsymfony/jsrouting-bundle/FOS/JsRoutingBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/avalanche123/imagine-bundle/Avalanche/Bundle/ImagineBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/genemu/form-bundle/Genemu/Bundle/FormBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/src/GergelyPolonkai/FrontBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/src/GergelyPolonkai/GeshiBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/symfony/symfony/src/Symfony/Bundle/WebProfilerBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/sensio/distribution-bundle/Sensio/Bundle/DistributionBundle' |  | ||||||
|     '/var/www/html/gergelypolonkaiweb/vendor/sensio/generator-bundle/Sensio/Bundle/GeneratorBundle'" exited with non-successful status code "2". |  | ||||||
|  |  | ||||||
| After getting through my logs and such, I’ve finally found out that the new |  | ||||||
| SELinux policy is causing the trouble together with git. Eventually, my |  | ||||||
| `.git/logs` directory is tagged as `unconfined_u:object_r:httpd_log_t:s0`. |  | ||||||
| `httpd_log_t` type is not readable by the `system_u:system_r:httpd_t:s0` user, |  | ||||||
| which makes `/usr/bin/grep` throw an access denied error. To fix this, I needed |  | ||||||
| to do |  | ||||||
|  |  | ||||||
|     semanage fcontext -a -t httpd_sys_content_t '/var/www(/.*)?/\.git/logs(/.*)?' |  | ||||||
|  |  | ||||||
| as root. This makes `.git` directories readable for the httpd process, thus, |  | ||||||
| for `grep`. The optimal solution would be to tell `GrepPatternFinder` to ignore |  | ||||||
| version control stuff, so the `httpd` process would have no access to them at |  | ||||||
| all. Also, in production, removing the `.git` or `.svn` directories could be a |  | ||||||
| good idea. |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "mount: device or resource busy after enabling multipath" |  | ||||||
| date:      2013-02-19 23:09:05 |  | ||||||
| tags:      [linux, heartbeat-cluster] |  | ||||||
| permalink: /blog/2013/2/19/mount-device-or-resource-busy-after-enabling-multipath |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| We have a heartbeat cluster with two nodes. It has been running for several |  | ||||||
| months without problems. The shared storage is on an IBM DS3400, on which we |  | ||||||
| have a large volume formatted with ext4. |  | ||||||
|  |  | ||||||
| Today I decided to reboot the active node for security reasons. So I’ve |  | ||||||
| switched to the passive node, which failed at the first step: it was unable to |  | ||||||
| mount the storage (`/dev/sda1`). After whining for a few moments, I tried to |  | ||||||
| mount it by hand, which told me |  | ||||||
|  |  | ||||||
|     /dev/sda1 already mounted or /data is busy |  | ||||||
|  |  | ||||||
| I’ve quickly made sure that none of that was true. After checking |  | ||||||
| this-and-that, it turned out that the passive node had `multipathd` running, so |  | ||||||
| I looked under `/dev/mapper`, and found two symlinks there, `<long-long WWN>` |  | ||||||
| and `<long-long WWN>-part1`. As the partition table and the disk size was the |  | ||||||
| same as on `/dev/sda`, I tried to |  | ||||||
|  |  | ||||||
|     mount /dev/<long-long WWN>-part1 /data |  | ||||||
|  |  | ||||||
| and voilà! It worked like charm! |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Why I stopped using annotation based routing in Symfony today" |  | ||||||
| date:      2013-02-27 23:10:24 |  | ||||||
| tags:      [development, symfony] |  | ||||||
| permalink: /blog/2013/2/27/why-i-stopped-using-annotation-based-routing-in-symfony-today |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I have read several opinions about routing configuration in Symfony. I stayed |  | ||||||
| with annotation based routing as it was convinient for me to see the URL right |  | ||||||
| above the controller action. This was because by just checking the URL, I |  | ||||||
| remembered the controlling code, as they always were fresh ones. Well, until |  | ||||||
| today. |  | ||||||
|  |  | ||||||
| I had to take a look into an old (Sf 2.0, last commit was about 3 months ago) |  | ||||||
| project of mine. In the same run I’ve upgraded the whole project to 2.2 (it was |  | ||||||
| a fast one, thanks for [JMikola@GitHub](https://github.com/jmikola) for the |  | ||||||
| quick reply on my issue with |  | ||||||
| [JmikolaJsAssetsHelperBundle](https://github.com/jmikola/JmikolaJsAssetsHelperBundle) |  | ||||||
| again!). After that I went on to the requested change. Now, finding a route in |  | ||||||
| about 40 controller files spread between 3 bundles can really be a pain! So |  | ||||||
| I’ve finished with annotation based routing. It’s still a nice feature, it’s |  | ||||||
| simply not for me. |  | ||||||
| @@ -1,62 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Programming, as I see it" |  | ||||||
| date:      2013-03-01 23:32:35 |  | ||||||
| permalink: /blog/2013/3/1/programming-as-i-see-it |  | ||||||
| published: false |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Since my age of around 11, I write code. I began with BASIC, which is, |  | ||||||
| well, the most basic language I have ever seen. Simply writing |  | ||||||
| <code>10 PRINT "Hello World!"</code> does the job (with Assembly it |  | ||||||
| would be tens of lines as I recall). Then I moved to Pascal, then |  | ||||||
| Delphi (which is basically the same thing). The next step was a bit |  | ||||||
| longer, as I started learning more languages after this, like Perl |  | ||||||
| (for dynamic web pages), C (for desktop applications), TCL (for |  | ||||||
| eggdrop programming. Yes, I might have been a weird kid), PHP (again, |  | ||||||
| for dynamic web pages. It was becoming mainstream back then). |  | ||||||
|  |  | ||||||
| Many of my classmates looked down on me, as they thought I was a geek (hell I |  | ||||||
| was, but I wouldn’t have confessed it then), and called me a nerd. For a few |  | ||||||
| months maybe I was depressed, but after that I realised that this is the thing |  | ||||||
| I want to do in my life, this is the thing I’m good at. |  | ||||||
|  |  | ||||||
| Most people I ask why don’t they code say “it’s too hard”. I’ve attended some |  | ||||||
| courses (both online and offline, and I was like “Whoa! Coding is extremely |  | ||||||
| hard! What the hell! I will never learn it!”, but right after the course I |  | ||||||
| realised that everything is just fine, I can still write programs, and it’s |  | ||||||
| eeeeasy. So then, what’s the problem? |  | ||||||
|  |  | ||||||
| After looking through many course papers, I found that most teachers do it |  | ||||||
| totally wrong. A programming language is just that: a language. You don’t start |  | ||||||
| learning Spanish by going into a classic literature conference in Madrid and |  | ||||||
| doing a speech, but learn the basic vocabulary and grammar. The same goes for |  | ||||||
| coding. You learn the vocabulary (the basic commands or keywords) and grammar |  | ||||||
| (syntax). I had several ideas how this could be taught, just didn’t have the |  | ||||||
| background to do it. |  | ||||||
|  |  | ||||||
| The idea of teaching programming lingers in my head for years now, and a few |  | ||||||
| days ago, I’ve bumped into [this |  | ||||||
| video](https://www.youtube.com/watch?v=dU1xS07N-FA). So it seems that |  | ||||||
| technology superstars like Bill Gates and Mark Zuckerberg wants to do the same. |  | ||||||
| Maybe they don’t have enough high quality coders at hand. Well of course, |  | ||||||
| if teachers make it awfully hard to learn it! So a bunch of guys sat together |  | ||||||
| and created [code.org](http://www.code.org/) to achieve my old dream. I like |  | ||||||
| the idea. And although I have almost no visitor on this blog of mine, allow me |  | ||||||
| to give you a few points on how I see programming. |  | ||||||
|  |  | ||||||
| ####  Great learning process |  | ||||||
|  |  | ||||||
| When you write programs, especially during the first years, you adapt a new way |  | ||||||
| of thinking and learning. If you learn it as an adult, it can be a bit of a |  | ||||||
| pain, but as a child, it’s easy as learning how the wheels of those little cars |  | ||||||
| spin). |  | ||||||
|  |  | ||||||
| ####  A job |  | ||||||
|  |  | ||||||
| ####  Art |  | ||||||
|  |  | ||||||
| ####  Magic |  | ||||||
| @@ -1,38 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Fedora can’t change Active Directory password via kpasswd" |  | ||||||
| date:      2013-03-05 08:55:04 |  | ||||||
| tags:      [fedora, kerberos, active-directory] |  | ||||||
| permalink: /blog/2013/3/5/fedora-can-t-change-active-directory-password-via-kpasswd |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I wanted to change my AD password today. As the AD is actually a Kerberos |  | ||||||
| server, I was pretty sure that `kpasswd` will do the trick. However, `kpasswd` |  | ||||||
| output looked like this: |  | ||||||
|  |  | ||||||
|     $ kpasswd |  | ||||||
|     Password for polonkai.gergely@EXAMPLE.LOCAL: |  | ||||||
|     Enter new password: |  | ||||||
|     Enter it again: |  | ||||||
|     kpasswd: Cannot find KDC for requested realm changing password |  | ||||||
|  |  | ||||||
| I’ve checked `kinit` and `klist`, everything looked fine. After a while it came |  | ||||||
| to my mind that password changing is done through the kadmin server, not |  | ||||||
| through the KDC. It seems that when I set up the Active Directory membership, |  | ||||||
| the `admin_server` directive is not get written to `krb5.conf`. So all I had to |  | ||||||
| do was to put |  | ||||||
|  |  | ||||||
|     admin_server = ad.example.local |  | ||||||
|  |  | ||||||
| in that file, and voilà! |  | ||||||
|  |  | ||||||
|     $ kpasswd |  | ||||||
|     Password for polonkai.gergely@EXAMPLE.LOCAL: |  | ||||||
|     Enter new password: |  | ||||||
|     Enter it again: |  | ||||||
|     Password changed. |  | ||||||
|  |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Haversine in MySQL" |  | ||||||
| date:      2013-03-05 12:49:28 |  | ||||||
| permalink: /blog/2013/3/5/haversine-in-mysql |  | ||||||
| tags:      [mysql, development] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Just insert it in your database, feed them two Google coordinates, and you get |  | ||||||
| the distance in kilometres. If you happen to need it in miles, change the |  | ||||||
| constant `12756.200` in the `RETURN` row to `7922.6` instead. |  | ||||||
|  |  | ||||||
| {% gist bdad1cf2d410853bef35 %} |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Dvorak and me" |  | ||||||
| date:      2013-03-13 21:20:13 |  | ||||||
| tags:      [linux] |  | ||||||
| permalink: /blog/2013/3/13/dvorak-and-me |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| A few months ago I have decided to switch to the Dvorak layout. After using |  | ||||||
| QWERTY (well, QWERTZ, to be precise) for almost 17 years, it was a hard |  | ||||||
| decision, but now I think it worthed the try. I started with the UK (Dvorak |  | ||||||
| with UK punctuation) layout, and in about four weeks, I’ve almost reached my |  | ||||||
| original typing speed. Today I have modified the Hungarian xkb definitions file |  | ||||||
| to add the Hungarian accended letters like ű to the layout, so I don’t have to |  | ||||||
| use dead keys anymore (which apparently turned out to be a problem, as the |  | ||||||
| Linux version of Java doesn’t support dead keys at all). |  | ||||||
|  |  | ||||||
| Best thing is, as I never learned proper 10-finger typing, but learned Dvorak |  | ||||||
| that way, I can switch between QWERTY and Dvorak more or less painlessly (about |  | ||||||
| 10 minutes of confusion, so to say). |  | ||||||
|  |  | ||||||
| Conclusion: I don’t know yet if this was actually a good decision, but it |  | ||||||
| wasn’t bad, after all. But seeing people’s faces when they try to type on my |  | ||||||
| machine totally worths it. |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Renaming a Symfony 2 bundle" |  | ||||||
| date:      2013-04-09 22:29:48 |  | ||||||
| tags:      [development, symfony] |  | ||||||
| permalink: /blog/2013/4/9/renaming-a-symfony-2-bundle |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Today I’ve realised that the name I gave to one of my Symfony 2 bundles should |  | ||||||
| be something else. To rename a bundle, one must do four things (at least). |  | ||||||
|  |  | ||||||
| 1. Change the namespace from `Vendor\OldBundle` to `Vendor\NewBundle` in every |  | ||||||
|    PHP class (sounds like pain? It is…) |  | ||||||
| 1. Change the name of files and classes. Some files under |  | ||||||
|    `src/Vendor/OldBundle` (and the classes in them) contain the name of the |  | ||||||
|    bundle, like `OldBundle/DependencyInjection/VendorOldBundleExtension.php` |  | ||||||
|    and `OldBundle/VendorOldBundle.php`. You should rename them, or Symfony |  | ||||||
|    won’t find the classes defined in them! When done, rename the whole bundle |  | ||||||
|    directory either. |  | ||||||
| 1. Change the configuration files accordingly, including `AppKernel.php`. These |  | ||||||
|    config files are usually `routing.yml`, `services.yml`, and in some cases, |  | ||||||
|    `config.yml` |  | ||||||
| 1. Change the references in other parts of your code. A `grep OldBundle .` will |  | ||||||
|    usually help… |  | ||||||
| @@ -1,111 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Installing OTRS in Fedora 18 with SELinux enabled" |  | ||||||
| date:      2013-05-06 06:01:52 |  | ||||||
| tags:      [fedora, selinux, otrs] |  | ||||||
| permalink: /blog/2013/5/6/installing-otrs-in-fedora-18-with-selinux-enabled |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I’ve read somewhere in an OTRS installation howto that if you want to install |  | ||||||
| OTRS, you will have to disable SELinux. Well, I won’t. |  | ||||||
|  |  | ||||||
| During the last few months, I have been using Fedora 18 with SELinux on all of |  | ||||||
| my desktop machines and on my notebook, and I had no problems at all. |  | ||||||
| Meanwhile I got familiar with SELinux itself, and got used to solving problems |  | ||||||
| caused by it. So I started `tail -f /var/log/httpd/error_log` in one terminal |  | ||||||
| (to see if something Apache related thing appears), |  | ||||||
| `tail -f /var/log/audit/audit.log` in another (to see errors caused by |  | ||||||
| SELinux), opened the admin manual at the installation chapter, took a deep |  | ||||||
| breath, and went on. |  | ||||||
|  |  | ||||||
| Throughout this article, I will refer to OTRS 3.2.6 as OTRS and Fedora 18 |  | ||||||
| (with only “stock” repositories) as Fedora. I assume that you have already |  | ||||||
| installed OTRS in a non-SELinux environment before, and that you have at least |  | ||||||
| some basic knowledge about SELinux, MAC, RBAC, and all the like. I’m |  | ||||||
| installing OTRS in `/opt/otrs`, so if you install it somewhere else, you will |  | ||||||
| have to modify the paths below. Also, if you happen to install under |  | ||||||
| `/var/www` (I wouldn’t recommend it), that directory already has the |  | ||||||
| `httpd_sys_content_t` type, so you won’t have to set it explicitly. |  | ||||||
|  |  | ||||||
| As the first step I have unpacked the archive to `/opt/otrs`. This directory |  | ||||||
| is the OTRS default, many config files have it hardcoded, and changing it is |  | ||||||
| no easy task. |  | ||||||
|  |  | ||||||
| Running `otrs.CheckModules.pl` gave me a list of missing perl modules. Red Hat |  | ||||||
| and Fedora makes it easy to install these, as you don’t have to know the RPM |  | ||||||
| package name, just the perl module name: |  | ||||||
|  |  | ||||||
|     yum install 'perl(Crypt::SSLeay)' \ |  | ||||||
|                 'perl(DBD::Pg)' \ |  | ||||||
|                 'perl(GD)' \ |  | ||||||
|                 'perl(JSON::XS)' \ |  | ||||||
|                 'perl(GD::Text)' \ |  | ||||||
|                 'perl(GD::Graph)' \ |  | ||||||
|                 'perl(Mail::IMAPClient)' \ |  | ||||||
|                 'perl(Net::DNS)' \ |  | ||||||
|                 'perl(PDF::API2)' \ |  | ||||||
|                 'perl(Text::CSV_XS)' \ |  | ||||||
|                 'perl(YAML::XS)' |  | ||||||
|  |  | ||||||
| I also needed to install `mod_perl`. Although `otrs.CheckModules.pl` didn’t |  | ||||||
| mention it, the default settings use syslog as the logging module, so unless |  | ||||||
| you change it in `Config.pm`, you will also need to install |  | ||||||
| `'perl(Unix::Syslog)'`, either. |  | ||||||
|  |  | ||||||
| The default SELinux policy doesn’t permit any network connection to be |  | ||||||
| initiated by Apache httpd. As OTRS needs to connect to its database, you |  | ||||||
| need to enable it explicitly. In older distributions, the |  | ||||||
| `httpd_can_network_connect` was the SELinux boolean for this, but recent |  | ||||||
| installations also have a `httpd_can_network_connect_db` flag. As far as I |  | ||||||
| know, this enables all network connections to the well-known database |  | ||||||
| servers’ default port, but I will have to check for it. For me, with a |  | ||||||
| MySQL listening on its standard port, the |  | ||||||
| `setsebool httpd_can_network_connect_db=1` command just did it. |  | ||||||
|  |  | ||||||
| With SELinux enabled, Apache won’t be able to read anything that’s not |  | ||||||
| marked with the `httpd_sys_content_t` type, nor write anywhere without the |  | ||||||
| `httpd_sys_rw_content_t` type. The trivial, quick and dirty solution is to |  | ||||||
| label all the files as `httpd_sys_rw_content_t`, and let everything go. |  | ||||||
| However, the goal of SELinux is just the opposite of this: grant access |  | ||||||
| only to what is really needed. After many trial-and-error steps, it finally |  | ||||||
| turned out that for OTRS to work correctly, you must set |  | ||||||
|  |  | ||||||
| * `httpd_sys_content_t` |  | ||||||
|   * on `/opt/otrs/var/httpd/htdocs` |  | ||||||
| * `httpd_script_exec_t` |  | ||||||
|   * on `/opt/otrs/bin/cgi-bin` |  | ||||||
| * `httpd_sys_rw_content_t` |  | ||||||
|   * on `/opt/otrs/Kernel` |  | ||||||
|   * on `/opt/otrs/var/sessions` |  | ||||||
|   * on `/opt/otrs/var/log` (unless you use syslog for logging) |  | ||||||
|   * on `/opt/otrs/var/packages` (this is used only when you download an .opm |  | ||||||
|     package) |  | ||||||
|   * on `/opt/otrs/var/stats` |  | ||||||
|   * on `/opt/otrs/var/tmp` |  | ||||||
|   * on `/opt/otrs/bin` (I wonder why this is required, though) |  | ||||||
|  |  | ||||||
| To do this, use the following command: |  | ||||||
|  |  | ||||||
|     # semanage fcontext -a -t <context> <directory regex> |  | ||||||
|  |  | ||||||
| Where `<directory regex>` is something like `/opt/otrs/Kernel(/.*)?`. When |  | ||||||
| this is done, all you have to do is running `restorecon -vR /opt/otrs` so |  | ||||||
| it will relabel everything with the correct types (you can omit -v, I just |  | ||||||
| like to see what my software does). |  | ||||||
|  |  | ||||||
| The last thing I faced is that Fedora is more restrictive on reading |  | ||||||
| directories other than `/var/www`. It has a `Require all denied` on |  | ||||||
| `<Directory />`, and a `Require all granted` on `<Directory /var/www>`, so |  | ||||||
| `/opt/otrs/var/httpd/htdocs` will throw a |  | ||||||
| `403 Forbidden (client denied by server configuration)` error. To get rid |  | ||||||
| of this, I had to modify `scripts/apache2-httpd.include.conf` and add |  | ||||||
| `Require all granted` to both the `cgi-bin` and `htdocs` directories. |  | ||||||
|  |  | ||||||
| As I will have to use OTRS in a production environment soon with SELinux |  | ||||||
| enabled, it is more than sure that this list will change in the near future. |  | ||||||
| As there are no official documentation on this (I haven’t find one yet), I |  | ||||||
| have to do it with the trial-and-error way, so be patient! |  | ||||||
| @@ -1,30 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "SWE-GLib final release" |  | ||||||
| date:      2013-09-16 21:37:17 |  | ||||||
| tags:      [development, astrology] |  | ||||||
| permalink: /blog/2013/9/16/swe-glib-final-release |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Few of you may know that I’m interested in astrology. About two months ago |  | ||||||
| I have decided to create an astrologers’ software for the GNOME desktop. |  | ||||||
| Since then, I have contacted Jean-André Santoni, who created a software |  | ||||||
| called [Astrognome](https://code.google.com/p/astrognome/) some years ago. |  | ||||||
| We exchanged some e-mails, and after several weeks of coding, I’m proud to |  | ||||||
| present [SWE-GLib](https://github.com/gergelypolonkai/swe-glib) 1.0.1. This |  | ||||||
| is “just” a library which wraps around [Swiss |  | ||||||
| Ephemeris](http://www.astro.com/swisseph/), creating a nice GLib-ish |  | ||||||
| interface around it. See the project page and the built-in GTK-Doc document |  | ||||||
| for more information. |  | ||||||
|  |  | ||||||
| The astrologer’s software I’m writing will be |  | ||||||
| [Astrognome](https://github.com/gergelypolonkai/astrognome) (thanks for |  | ||||||
| Jean-André for letting me use the name). It is currently in pre-alpha |  | ||||||
| status, but already utilizes SWE-GLib (it just can’t display the results |  | ||||||
| yet). If you happen to be interested in astrology and/or Astrognome, fork |  | ||||||
| the repository and contribute! You can also contact me (or open an |  | ||||||
| enhancement issue on GitHub) if you have any ideas. |  | ||||||
| @@ -1,25 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "From Symfony to Django in two days" |  | ||||||
| date:      2013-09-24 14:05:22 |  | ||||||
| tags:      [development, symfony, django] |  | ||||||
| permalink: /blog/2013/9/24/from-symfony-to-django-in-two-days |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I was a Python hater for a long time, although I can’t really tell why. It |  | ||||||
| didn’t fit in my mind, maybe. I was programming in BASIC, Pascal (none of |  | ||||||
| these would come to my mind, though), C, PHP, Perl, JavaScript, and |  | ||||||
| different shell “languages” like awk, sed or bash. |  | ||||||
|  |  | ||||||
| After I could not fit my next Symfony app on my cloud server (it is pretty |  | ||||||
| low on storage), I have decided to move slowly to Django. My first task was |  | ||||||
| simple: transition my web page (this one) from PHP + Symfony 2 to Python + |  | ||||||
| Django. The results: the “static” pages are already working, the blog |  | ||||||
| listing is almost ready (some styling issues are still around), only |  | ||||||
| tagging remains. And this is after about 6 hours of work. Oh, and the admin |  | ||||||
| site is included with Django, so I don’t have to port that. I have also |  | ||||||
| decided to finally integrate a comment feature in the Django version. |  | ||||||
| @@ -1,29 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "First impressions of Windows 8" |  | ||||||
| date:      2013-11-05 08:14:50 |  | ||||||
| tags:      [windows] |  | ||||||
| permalink: /blog/2013/11/5/first-impressions-of-windows-8 |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Many of you may know my commitment to Linux and Open Source Software. But this |  | ||||||
| doesn’t mean I hate proprietary software like many others do. I think |  | ||||||
| everything has its own place in the world, and this goes for software as well. |  | ||||||
|  |  | ||||||
| A few days ago I got my hands on a new notebook, thanks to my company. It was |  | ||||||
| shipped with Windows 8 by default, and although I installed Fedora 19 in an |  | ||||||
| instant (which went smoothlessly, even with Secure Boot enabled), I’ve decided |  | ||||||
| to give a try to this new Windows Version. |  | ||||||
|  |  | ||||||
| Being a heavy Windows 7 user, my first thought was “What the hell is this?” |  | ||||||
| But in a day, I got totally used to it. I don’t miss the Start button at all. |  | ||||||
| The applications already installed were almost enough for me (I still need |  | ||||||
| Office. Maybe I’ll also enroll for Office 365 later…), and the games are great |  | ||||||
| and beautiful too. So overall, this new version may be totally different (by |  | ||||||
| the looks), but it seems almost the same Windows as we know it. So if you |  | ||||||
| don’t freak out by touching something new, go give it a try: don’t |  | ||||||
| instant-remove 8 in favour of 7! |  | ||||||
| @@ -1,33 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "List Git branches and their remote tracking branches side by side" |  | ||||||
| date:      2014-07-18 21:46:45 |  | ||||||
| tags:      [git] |  | ||||||
| permalink: /blog/2014/7/18/list-git-branches-and-their-remote-tracking-branches-side-by-side |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I had a hard time following my own branches in a project. They got pretty |  | ||||||
| numerous, and I wasn’t sure if I pushed them to origin at all. |  | ||||||
| `git branch -a` can list all the branches, including remote ones, but, as |  | ||||||
| my list grew too big, it was impossible to follow it any more. |  | ||||||
|  |  | ||||||
| Thus, I have created a small script called git-branches-with-remotes, which |  | ||||||
| does the work for me. Its only requirements are git (of course), and the |  | ||||||
| `column` command, which is pretty obviously present on every POSIX |  | ||||||
| compliant systems (even OSX). |  | ||||||
|  |  | ||||||
| {% gist 8af6a3e86b57dd4c250e %} |  | ||||||
|  |  | ||||||
| I just put it in my path, and `git branches-with-remotes` does the work! |  | ||||||
|  |  | ||||||
| Edit (16 August): I have added some code to mark the current branch (if any) |  | ||||||
| with an asterisk. Also, I have put this script [in a |  | ||||||
| gist](https://gist.github.com/gergelypolonkai/8af6a3e86b57dd4c250e). |  | ||||||
|  |  | ||||||
| Edit (26 February, 2015): It turns out that `git branch -vv` shows the same |  | ||||||
| information and some more: it also shows if the branches are diverged, and the |  | ||||||
| first line of the last commit’s message. |  | ||||||
| @@ -1,36 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Registering an enum type in GLib, glib-mkenums magic" |  | ||||||
| date:      2014-08-16 15:10:54 |  | ||||||
| tags:      [development, c, glib] |  | ||||||
| permalink: /blog/2014/8/16/registering-an-enum-type-in-glib-glib-mkenums-magic |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| In [this |  | ||||||
| post](/blog/2013/1/6/registering-an-enum-type-in-glib-s-type-system) I said |  | ||||||
| I will get through the GLib Makefiles to add an enum type to GLib in a more |  | ||||||
| sophisticated way. |  | ||||||
|  |  | ||||||
| In my other project, |  | ||||||
| [SWE-GLib](https://github.com/gergelypolonkai/swe-glib) I already used this |  | ||||||
| method. The following two rules in `Makefile.am` create `gswe-enumtypes.h` |  | ||||||
| and `gswe-enumtypes.c`. |  | ||||||
|  |  | ||||||
| {% gist 1e2fdedb136de3ca67f0 Makefile %} |  | ||||||
|  |  | ||||||
| `$(GLIB_MKENUMS)` is set in `configure` with |  | ||||||
| `AC_PATH_PROG([GLIB_MKENUMS], [glib-mkenums])`. |  | ||||||
|  |  | ||||||
| This approach requires the GNU Autotools (you can get rid of it by changing |  | ||||||
| `$(GLIB_MKENUMS)` to the path to `glib-mkenums` binary), and two template |  | ||||||
| files, one for the header and one for the code. `$(gswe_enum_headers)` |  | ||||||
| contains a list of all the header files that have enum types defined |  | ||||||
| throughout the project. |  | ||||||
|  |  | ||||||
| {% gist 1e2fdedb136de3ca67f0 gswe-enumtypes.h %} |  | ||||||
|  |  | ||||||
| {% gist 1e2fdedb136de3ca67f0 gswe-enumtypes.c %} |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "NyanMacs" |  | ||||||
| date:      2014-09-17 12:45:42 |  | ||||||
| tags:      [emacs] |  | ||||||
| permalink: /blog/2014/9/17/nyanmacs |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I was a Vi/ViM user for years. For several reasons I had to change to Emacs |  | ||||||
| now and then. And then, I found |  | ||||||
| [this](https://www.emacswiki.org/emacs/NyanMode). I surrender. Emacs is |  | ||||||
| just better. (And it’s working even in plain text mode without graphics) |  | ||||||
| @@ -1,25 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Rounding numbers to N decimals in Emacs" |  | ||||||
| date:      2014-10-07 10:28:50 |  | ||||||
| tags:      [emacs, development] |  | ||||||
| permalink: /blog/2014/10/7/rounding-numbers-to-n-decimals-in-emacs |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I have recently faced a problem, where I had a bunch of SVG files with a |  | ||||||
| large amount of fraction numbers in the path definitions. These images were |  | ||||||
| displayed in small size, so this amount of precision was irrelevant, and |  | ||||||
| these numbers took almost half of my SVG images’ size. So I created an |  | ||||||
| Elisp defun to round these numbers to 2 decimals: |  | ||||||
|  |  | ||||||
| {% gist 9c721ceda6d3079b4f05 %} |  | ||||||
|  |  | ||||||
| This finds the first digit of the number under point (the cursor), and |  | ||||||
| reduces its digits to the given amount (or the number given with `C-u`). It |  | ||||||
| has some drawbacks, though, as it cannot handle exponential forms (e.g. |  | ||||||
| `1e-1234`), but these were rare in my case, and its hard to iterate through |  | ||||||
| all numbers. I will come over this latter problem soon(ish). |  | ||||||
| @@ -1,46 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Using Git bisect to find the first good commit" |  | ||||||
| date:      2015-02-26 10:42:56 |  | ||||||
| tags:      [git] |  | ||||||
| permalink: /blog/2015/2/26/using-git-bisect-to-find-the-first-good-commit |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Few months ago we “implemented” a bug in our software, which was released |  | ||||||
| to the customers. We continued development for two weeks when the first |  | ||||||
| customer ticket arrived about the bug. We successfully reproduced it with |  | ||||||
| the customer’s version, but not with the development sources; it turned out |  | ||||||
| that one of the developers unconsciously fixed the bug. The devs spent some |  | ||||||
| hours finding where the fix lied before coming to me like “There is |  | ||||||
| `git-bisect` which we can use to find the commit where we messed up things. |  | ||||||
| Is there a way to find where we fixed it?” |  | ||||||
|  |  | ||||||
| For those who don’t know this feature, you have to mark a known “good” and |  | ||||||
| “bad” commit, then git-bisect will go through the commits between this two, |  | ||||||
| present you the corresponding snapshots, and you have to mark each of them |  | ||||||
| as “good” or “bad”. At the end, you will get a commit hash where the bug |  | ||||||
| first occured. |  | ||||||
|  |  | ||||||
| As it turned out, our developers’ problem rooted in the naming convention |  | ||||||
| of git-bisect: they assumed that the “good” commit must be a working one, |  | ||||||
| while a “bad” one must be the buggy. In this case, we did the following: |  | ||||||
|  |  | ||||||
| The commit with the customer’s release tag was marked as good (even though |  | ||||||
| this had the bug), and the latest commit on our development branch was |  | ||||||
| marked as “bad” (even though the bug was fixed by then). Now with every |  | ||||||
| snapshot presented by git-bisect we had to do the opposite what you usually |  | ||||||
| do: mark commits still having the bug as “good”, and commits that don’t as |  | ||||||
| “bad”. At the end, we had the hash of the commit that fixed the bug (among |  | ||||||
| some other things; luckily, the developer who pushed that commit had a |  | ||||||
| workflow that introduced a lot of cherry-picking and squashing before the |  | ||||||
| push, so he could easily find the bit that actually fixed the problem in |  | ||||||
| his local repository with the same technique). |  | ||||||
|  |  | ||||||
| [This StackOverflow answer](http://stackoverflow.com/a/17153598/1305139) |  | ||||||
| suggests the very same, but with some aliases: |  | ||||||
|  |  | ||||||
| {% gist a98f4aab84659d60364e %} |  | ||||||
| @@ -1,23 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Good bye, Digital Ocean! Hello again, GitHub!" |  | ||||||
| date:      2015-04-25 21:18:56 |  | ||||||
| permalink: /blog/2015/4/25/good-bye-digital-ocean-hello-again-github |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Few years ago I have signed up for a |  | ||||||
| [Digital Ocean](https://www.digitalocean.com/) account. I used one |  | ||||||
| single droplet for my private needs, like hosting my private Git |  | ||||||
| repositories and my blog. However, as I didn’t host anything else there |  | ||||||
| except my blog, I decided to shut it down. From now on, my blog is |  | ||||||
| on [GitHub Pages](https://pages.github.com/), as it provides just |  | ||||||
| everything I need (except automatically converting my resume to |  | ||||||
| PDF. But I can live without that.) |  | ||||||
|  |  | ||||||
| I’m really sorry, Digital Ocean Guys, your hosting is awesome and I’ll |  | ||||||
| keep recommending you to others, but paying for a droplet for one |  | ||||||
| single blog is overkill. |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Cross browser border-radius SASS mixin with varargs" |  | ||||||
| date:      2015-04-27 22:59:56 |  | ||||||
| tags:      [css, sass] |  | ||||||
| permalink: /blog/2015/4/28/cross-browser-border-radius-sass-mixin-with-varargs |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Few days ago I needed to create style sheets with many rounded boxes, |  | ||||||
| where different corners had to be rounded differently (think about |  | ||||||
| Bootstrap’s [button |  | ||||||
| groups](http://getbootstrap.com/components/#btn-groups)). |  | ||||||
|  |  | ||||||
| CSS has this nifty shorthand to specify border width in one line, like |  | ||||||
| with `border-width: 1px 2px 3px 4px`, but it lacks the same for |  | ||||||
| `border-radius`. So I decided to create something similar using [Sass |  | ||||||
| mixins](http://sass-lang.com/guide#topic-6) with dynamic |  | ||||||
| parameters. Another nice feature you get using the `border-width` |  | ||||||
| shorthand is that you can specify less than four parameters, and the |  | ||||||
| values will be applied on different sides of your box, so in the end |  | ||||||
| all side will have the whole `border-width` set. |  | ||||||
|  |  | ||||||
| I wanted to achieve the same for my `border-radius` mixin, although I |  | ||||||
| could not start specifically with the `top` side. I decided to go with |  | ||||||
| the top right corner for the first parameter, while trying to keep a |  | ||||||
| sane repeating pattern. Here is the result: |  | ||||||
|  |  | ||||||
| {% gist 313b227434ecc5d85d7b border-radius.sass %} |  | ||||||
| @@ -1,39 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "@ParamConverter à la Django" |  | ||||||
| date:      2015-06-07 18:14:32 |  | ||||||
| tags:      [python, django] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name:  Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
| One thing I really miss from [Django](https://www.djangoproject.com/) |  | ||||||
| is [Symfony](http://symfony.com)’s |  | ||||||
| [@ParamConverter](http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html). It |  | ||||||
| made my life so much easier while developing with Symfony. In Django, |  | ||||||
| of course, there is |  | ||||||
| [get_object_or_404](https://docs.djangoproject.com/en/dev/topics/http/shortcuts/#get-object-or-404), |  | ||||||
| but, for example, in one of my projects I had a view that had to resolve 6(!) |  | ||||||
| objects from the URL, and writing `get_object_or_404` six times is not what a |  | ||||||
| programmer likes to do (yes, this view had a refactor later on). A quick Google |  | ||||||
| search gave me one [usable |  | ||||||
| result](http://openclassrooms.com/forum/sujet/middleware-django-genre-paramconverter-doctrine) |  | ||||||
| (in French), but it was very generalized that I cannot always use. Also, it was |  | ||||||
| using a middleware, which may introduce performance issues |  | ||||||
| sometimes<sup>[citation needed]</sup>. So I decided to go with decorators, and |  | ||||||
| at the end, I came up with this: |  | ||||||
|  |  | ||||||
| {% gist 498a32297f39b4960ad7 helper.py %} |  | ||||||
|  |  | ||||||
| Now I can decorate my views, either class or function based, with |  | ||||||
| `@convert_params(User, (Article, 'aid'), (Paragraph, None, 'pid'), |  | ||||||
| (AnotherObject, None, None, 'obj'))` and all the magic happens in the |  | ||||||
| background. The `user_id` parameter passed to my function will be |  | ||||||
| popped off, and be resolved against the `User` model by using the `id` |  | ||||||
| field; the result is put in the new `user` parameter. For Article, the |  | ||||||
| `aid` parameter will be matched against the `id` field of the |  | ||||||
| `Article` model putting the result into `article`, and finally, the |  | ||||||
| `another_object_id` will be matched against the `id` field of the |  | ||||||
| `AnotherObject` model, but in this case, the result is passed to the |  | ||||||
| original function as `obj`. |  | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "F/OSS Fail meter" |  | ||||||
| date:      2015-08-19 10:12:19 |  | ||||||
| tags:      [development] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I have recently bumped into [this |  | ||||||
| article](http://spot.livejournal.com/308370.html). Naturally, I quickly |  | ||||||
| calculated the FAIL metrics for all my projects (most of them are pretty high). |  | ||||||
| To ease calculation, I made up a |  | ||||||
| [small page]({{ '/failmeter/' | prepend: site.baseurl }}) based on this list |  | ||||||
| (although I have divided the points by 5; I really don’t understand why spot is |  | ||||||
| using such big points if all of them can be divided by 5). Feel free to use it, |  | ||||||
| and if you have any recommendations (point additions/removal, new categories, |  | ||||||
| etc.), leave me a comment! |  | ||||||
| @@ -1,228 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:    "How my e-mail gets to that other guy?" |  | ||||||
| date:      2015-08-27 21:47:19 |  | ||||||
| tags:      [technology] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| A friend of mine asked me how it is possible that she pushes buttons on her |  | ||||||
| keyboard and mouse, and in an instant her peer reads the text she had in her |  | ||||||
| mind. This is a step-by-step introduction of what happens in-between. |  | ||||||
|  |  | ||||||
| #### From your mind to your computer |  | ||||||
|  |  | ||||||
| When you decide to write an e-mail to an acquaintance of yours, you open up |  | ||||||
| your mailing software (this document doesn’t cover using mail applications |  | ||||||
| you access through your browsers, just plain old Thunderbird, Outlook or |  | ||||||
| similar programs. However, it gets the same after the mail left your |  | ||||||
| computer), and press the “New Mail” button. What happens during this process |  | ||||||
| is not covered in this article, but feel free to ask me in a comment! Now |  | ||||||
| that you have your Mail User Agent (MUA) up and running, you begin typing. |  | ||||||
|  |  | ||||||
| When you press a button on your keyboard or mouse, a bunch of bits gets |  | ||||||
| through the wire (or through air, if you went wireless) and get into your |  | ||||||
| computer. I guess you learned about Morse during school; imagine two |  | ||||||
| [Morse operators](http://www.uscupstate.edu/academics/education/aam/lessons/susan_sawyer/morse%20code.jpg), |  | ||||||
| one in your keyboard/mouse, and one in your computer. Whenever you press a |  | ||||||
| key, that tiny creature sends a series of short and long beeps (called 0 or |  | ||||||
| 1 bits, respectively) to the operator in your computer (fun fact: have you |  | ||||||
| ever seen someone typing at an amazing speed of 5 key presses per second? |  | ||||||
| Now imagine that whenever that guy presses a key on their keyboard, that |  | ||||||
| tiny little Morse operator pressing his button 16 times for each key press, |  | ||||||
| with perfect timing so that the receiving operator can decide if that was a |  | ||||||
| short or long beep.) |  | ||||||
|  |  | ||||||
| Now that the code got to the operator inside the machine, it’s up to him to |  | ||||||
| decode it. The funny thing about keyboards and computers is that the |  | ||||||
| computer doesn’t receive the message “Letter Q was pressed”, but instead |  | ||||||
| “The second button on the second row was pressed” (a number called scan |  | ||||||
| code). At this time the operator decodes this information (in this example |  | ||||||
| it is most likely this Morse code: `···-···· -··-····`) and checks one of |  | ||||||
| his tables titled “Current Keyboard Layout.” It says this specific key |  | ||||||
| corresponds to letter ‘Q’, so it forwards this information (I mean the |  | ||||||
| letter; after this step your computer doesn’t care which plastic slab you |  | ||||||
| hit, just the letter ‘Q’) to your MUA, inserts it into the mail in its |  | ||||||
| memory, then displaying it happily (more about this step later). |  | ||||||
|  |  | ||||||
| When you finish your letter you press the send button of your MUA. First it |  | ||||||
| converts all the pretty letters and pictures to something a computer can |  | ||||||
| understand (yes, those Morse codes, or more precisely, zeros and ones, |  | ||||||
| again). Then it adds loads of meta data, like your name and e-mail address, |  | ||||||
| the current date and time including the time zone and pass it to the sending |  | ||||||
| parts of the MUA so the next step can begin. |  | ||||||
|  |  | ||||||
| #### IP addresses, DNS and protocols |  | ||||||
|  |  | ||||||
| The Internet is a huge amount of computers connected with each other, all of |  | ||||||
| them having at least one address called IP address that looks something like |  | ||||||
| this: `123.234.112.221`. These are four numbers between 0 and 255 inclusive, |  | ||||||
| separated by dots. This makes it possible to have 4,294,967,296 computers. |  | ||||||
| With the rules of address assignment added, this is actually reduced to |  | ||||||
| 3,702,258,432; a huge number, still, but it is not enough, as in the era of |  | ||||||
| the Internet of Things everything is interconnected, up to and possibly |  | ||||||
| including your toaster. Thus, we are slowly transitioning to a new |  | ||||||
| addressing scheme that looks like this: |  | ||||||
| `1234:5678:90ab:dead:beef:9876:5432:1234`. This gives an enormous amount of |  | ||||||
| 340,282,366,920,938,463,463,374,607,431,768,211,456 addresses, with only |  | ||||||
| 4,325,185,976,917,036,918,000,125,705,034,137,602 of them being reserved, |  | ||||||
| which gives us only a petty |  | ||||||
| 335,957,180,944,021,426,545,374,481,726,734,073,854 available. |  | ||||||
|  |  | ||||||
| Imagine a large city with |  | ||||||
| [that many buildings](http://www.digitallifeplus.com/wp-content/uploads/2012/07/new-york-city-aerial-5.jpg), |  | ||||||
| all of them having only a number: their IP address. No street names, no |  | ||||||
| company names, no nothing. But people tend to be bad at memorizing numbers, |  | ||||||
| so they started to give these buildings names. For example there is a house |  | ||||||
| with the number `216.58.209.165`, but between each other, people call it |  | ||||||
| `gmail.com`. Much better, isn’t it? Unfortunately, when computers talk, they |  | ||||||
| only understand numbers so we have to provide them just that. |  | ||||||
|  |  | ||||||
| As remembering this huge number of addresses is a bit inconvenient, we |  | ||||||
| created Domain Name Service, or DNS for short. A “domain name” usually (but |  | ||||||
| not always) consist of two strings of letters, separated by dots (e.g. |  | ||||||
| polonkai.eu, gmail.com, my-very-long-domain.co.uk, etc.), and a hostname is |  | ||||||
| a domain name occasionally prefixed with something (e.g. **www**.gmail.com, |  | ||||||
| **my-server**.my-very-long-domain.co.uk, etc.) One of the main jobs of DNS |  | ||||||
| is to keep record of hostname/address pairs. When you enter `gmail.com` |  | ||||||
| (which happens to be both a domain name and a hostname) in your browser’s |  | ||||||
| address bar, your computer asks the DNS service if it knows the actual |  | ||||||
| address of the building that people call `gmail.com`. If it does, it will |  | ||||||
| happily tell your computer the number of that building. |  | ||||||
|  |  | ||||||
| Another DNS job is to store some meta data about these domain names. For |  | ||||||
| such meta data there are record types, one of these types being the Mail |  | ||||||
| eXchanger, or MX. This record of a domain tells the world who is handling |  | ||||||
| incoming mails for the specified domain. For `gmail.com` this is |  | ||||||
| `gmail-smtp-in.l.google.com` (among others; there can be multiple records of |  | ||||||
| the same type, in which case they usually have priorities, too.) |  | ||||||
|  |  | ||||||
| One more rule: when two computers talk to each other they use so called |  | ||||||
| protocols. These protocols define a set of rules on how they should |  | ||||||
| communicate; this includes message formatting, special code words and such. |  | ||||||
|  |  | ||||||
| #### From your computer to the mail server |  | ||||||
|  |  | ||||||
| Your MUA has two settings called SMTP server address SMTP port number (see |  | ||||||
| about that later). SMTP stands for Simple Mail Transfer Protocol, and |  | ||||||
| defines the rules on how your MUA, or another mail handling computer should |  | ||||||
| communicate with a mail handling computer when *sending* mail. Most probably |  | ||||||
| your Internet Service Provider gave you an SMTP server name, like |  | ||||||
| `smtp.aol.com` and a port number like `587`. |  | ||||||
|  |  | ||||||
| When you hit that send button of yours, your computer will check with the |  | ||||||
| DNS service for the address of the SMTP server, which, for `smtp.aol.com`, |  | ||||||
| is `64.12.88.133`. The computer puts this name/address pair into its memory, |  | ||||||
| so it doesn’t have to ask the DNS again (this technique is called caching |  | ||||||
| and is widely used wherever time consuming operations happen). |  | ||||||
|  |  | ||||||
| Then it will send your message to the given port number of this newly |  | ||||||
| fetched address. If you imagined computers as office buildings, you can |  | ||||||
| imagine port numbers as departments and there can be 65535 of them in one |  | ||||||
| building. The port number of SMTP is usually 25, 465 or 587 depending on |  | ||||||
| many things we don’t cover here. Your MUA prepares your letter, adding your |  | ||||||
| e-mail address and the recipients’, together with other information that may |  | ||||||
| be useful for transferring your mail. It then puts this well formatted |  | ||||||
| message in an envelope and writes “to building `64.12.88.133`, dept. `587`”, |  | ||||||
| and puts it on the wire so it gets there (if the wire is broken, the |  | ||||||
| building does not exist or there is no such department, you will get an |  | ||||||
| error message from your MUA). Your address and the recipient’s address are |  | ||||||
| inside the envelope; other than the MUA, your own computer is not concerned |  | ||||||
| about it. |  | ||||||
|  |  | ||||||
| The mailing department (or instead lets call it the Mail Transfer Agent, |  | ||||||
| A.K.A. MTA) now opens this envelope and reads the letter. All of it, letter |  | ||||||
| by letter, checking if your MUA formatted it well. More than probably it |  | ||||||
| also runs your message through several filters to decide if you are a bad |  | ||||||
| guy sending some unwanted letter (also known as spam), but most importantly |  | ||||||
| it fetches the recipients address. It is possible, e.g. when you send an |  | ||||||
| e-mail within the same organization, that the recipient’s address is handled |  | ||||||
| by this very same computer. In this case the MTA puts the mail to the |  | ||||||
| recipient’s mailbox and the next step is skipped. |  | ||||||
|  |  | ||||||
| #### From one server to another |  | ||||||
|  |  | ||||||
| Naturally, it is possible to send an e-mail from one company to another, so |  | ||||||
| these MTAs don’t just wait for e-mails from you, but also communicate with |  | ||||||
| each other. When you send a letter from your `example@aol.com` address to me |  | ||||||
| at `gergely@polonkai.eu`, this is what happens. |  | ||||||
|  |  | ||||||
| In this case, the MTA that initially received the e-mail from you (which |  | ||||||
| happened to be your Internet Service Provider’s SMTP server) turns to the |  | ||||||
| DNS again. It will ask for the MX record of the domain name specified by the |  | ||||||
| e-mail address, (the part after the `@` character, in my case, |  | ||||||
| `polonkai.eu`), because the server mentioned there must be contacted, so |  | ||||||
| they can deliver your mail for me. My domain is configured so its primary MX |  | ||||||
| record is `aspmx.l.google.com` and the secondary is |  | ||||||
| `alt1.aspmx.l.google.com` (and 5 more. Google likes to play it safe.) The |  | ||||||
| MTA then gets the first server name, asks the DNS for its address, and tries |  | ||||||
| to send a message to the `173.194.67.27` (the address of |  | ||||||
| `aspmx.l.google.com`), same department. But unlike your MUA, MTAs don’t have |  | ||||||
| a pre-defined port number for other MTAs (although there can be exceptions). |  | ||||||
| Instead, they use well-known port numbers, `465` and `25`. If the MTA on |  | ||||||
| that server cannot be contacted for any reason, it tries the next one on the |  | ||||||
| list of MX records. If none of the servers can be contacted, it will retry |  | ||||||
| based on a set of rules defined by the administrators, which usually means |  | ||||||
| it will retry after 1, 4, 24 and 48 hours. If there is still no answer after |  | ||||||
| that many attempts, you will get an error message back, in the form of an |  | ||||||
| e-mail sent directly by the SMTP server. |  | ||||||
|  |  | ||||||
| Once the other MTA could be contacted, your message is sent there. The |  | ||||||
| original envelope you used is discarded, and a new one is used with the |  | ||||||
| address and dept. number (port) of the receiving MTA. Also, your message |  | ||||||
| gets altered a little bit, as most MTAs are kind enough (ie. not sneaky) to |  | ||||||
| add a clause to your message stating “the MTA at <organization> has checked |  | ||||||
| and forwarded this message.” |  | ||||||
|  |  | ||||||
| It is possible, though not likely, that your message gets through more than |  | ||||||
| two MTAs (one at your ISP and one at the receiver’s) before arriving to its |  | ||||||
| destination. At the end, an MTA will say that “OK, this recipient address is |  | ||||||
| handled by me”, your message stops and stays there, put in your peer’s |  | ||||||
| mailbox. |  | ||||||
|  |  | ||||||
| ##### The mailbox |  | ||||||
|  |  | ||||||
| Now that the MTA has passed your mail to the mailbox team (I call it a team |  | ||||||
| instead of department because the tasks described here are usually handled |  | ||||||
| by the MTA, too), it reads it. (Pesky little guys are these mail handling |  | ||||||
| departments, aren’t they?) If the mailbox has some filtering rules, like “if |  | ||||||
| XY sends me a letter, mark it as important” or “if the letter has a specific |  | ||||||
| word in its subject, put it in the XY folder”, it executes them, but the |  | ||||||
| main point is to land the message in the actual post box of the recipient. |  | ||||||
|  |  | ||||||
| #### From the post box to the recipients computer |  | ||||||
|  |  | ||||||
| When the recipient opens their MUA, it will look to a setting usually called |  | ||||||
| “Incoming mail server”. Just like the SMTP server, it has a name and port |  | ||||||
| number, along with a server type. This type can vary from provider to |  | ||||||
| provider, and is usually one of POP3 (pretty old protocol, doesn’t even |  | ||||||
| support folders on its own), IMAP (a newer one, with folders and message |  | ||||||
| flags like “important”), MAPI (a dialect of IMAP, created by Microsoft as |  | ||||||
| far as I know), or plain old mbox files on the receiving computer (this last |  | ||||||
| option is pretty rare nowadays, so I don’t cover this option. Also, if you |  | ||||||
| use these, you most probably don’t really need this article to understand |  | ||||||
| how these things work.) This latter setting defines the protocol, telling |  | ||||||
| your MUA how to “speak” to the post box. |  | ||||||
|  |  | ||||||
| So your MUA turns to the DNS once more to get the address of your incoming |  | ||||||
| mail server and contacts it, using the protocol set by the server type. At |  | ||||||
| the end, the recipients computer will receive a bunch of envelopes including |  | ||||||
| the one that contains your message. The MUA opens them one by one and reads |  | ||||||
| them, making a list ordered by their sender or subject, or the date of |  | ||||||
| sending. |  | ||||||
|  |  | ||||||
| #### From the recipient’s comupter to their eyes |  | ||||||
|  |  | ||||||
| When the recipient then clicks on one of these mails, the MUA will fetch all |  | ||||||
| the relevant bits like the sender, the subject line, the date of sending and |  | ||||||
| the contents itself and sends it to the “printing” department (I use quotes |  | ||||||
| as they don’t really print your mail on paper, they just convert it to a |  | ||||||
| nice image so the recipient can see it. This is sometimes referred to as a |  | ||||||
| rendering engine). Based on a bunch of rules they pretty-print it and send |  | ||||||
| it to your display as a new series of Morse codes. Your display then decides |  | ||||||
| how it will present it to the user: draw the pretty pictures if it is a |  | ||||||
| computer screen, or just raise and lower some hard dots that represents |  | ||||||
| letters on a Braille terminal. |  | ||||||
| @@ -1,53 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Emacs: Implement a GObject’s virtual function" |  | ||||||
| date:      2016-01-13 13:31:12 |  | ||||||
| tags:      [c, development, emacs] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|   name: "Gergely Polonkai" |  | ||||||
|   email: "gergely@polonkai.eu" |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I have recently started creating a GLib implementation of the |  | ||||||
| Matrix.org API. For that, I have created a GObject interface, |  | ||||||
| MatrixAPI, which has as many virtual functions as API calls (which is |  | ||||||
| a lot, and expanding). This way I ended up with the following scenario. |  | ||||||
|  |  | ||||||
| In `matrix-api.h` I had a struct like this, with a lot more elements: |  | ||||||
|  |  | ||||||
|     typedef struct { |  | ||||||
|         void (*initial_sync)(MatrixAPI *api, |  | ||||||
|                              MatrixAPICallback callback, |  | ||||||
|                              gpointer user_data, |  | ||||||
|                              GError **error); |  | ||||||
|         void (*sync)(MatrixAPI *api, |  | ||||||
|                      MatrixAPICallback callback, |  | ||||||
|                      gpointer user_data, |  | ||||||
|                      GError **error); |  | ||||||
|         … |  | ||||||
|  |  | ||||||
| And in `matrix-http-api.c`, which implements `MatrixAPI`, I have a |  | ||||||
| function like this (again, with a lot more elements): |  | ||||||
|  |  | ||||||
|     static void |  | ||||||
|     matrix_http_api_matrix_api_init(GObjectInterface *iface) |  | ||||||
|     { |  | ||||||
|         iface->initial_sync = i_initial_sync; |  | ||||||
|         iface->sync = i_sync; |  | ||||||
|         … |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| And every time I wanted to implement a new function from the vtable, I |  | ||||||
| had to copy the prototype, and add an `iface->foo_bar = i_foo_bar` |  | ||||||
| line and an actual function header for `i_foo_bar` with the same |  | ||||||
| parameters. That’s a cumbersome job for more than 40 function |  | ||||||
| headers. But emacs comes to the rescue! |  | ||||||
|  |  | ||||||
| {% gist bfd36be8b515edced3d2 implement-gobject-vfunc.el %} |  | ||||||
|  |  | ||||||
| Now all I have to do is to copy the whole vtable entry into |  | ||||||
| `matrix_http_api_matrix_api_init()`, execute `M-x |  | ||||||
| implement-gobject-vfunc`, then put the same vtable entry somewhere |  | ||||||
| before the interface init function, and execute `M-x |  | ||||||
| implement-gobject-vfunc-prototype`. |  | ||||||
| @@ -1,33 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Vala interface madness" |  | ||||||
| date:      2016-02-26 13:07:52 |  | ||||||
| tags:      [vala, development] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|   name: "Gergely Polonkai" |  | ||||||
|   email: "gergely@polonkai.eu" |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Although I have just started making it in C, I decided to move my |  | ||||||
| Matrix GLib SDK to Vala. First to learn a new language, and second |  | ||||||
| because it is much easier to write GObject based stuff with it. |  | ||||||
|  |  | ||||||
| For the first step I created a `.vapi` file from my existing sources, |  | ||||||
| so the whole SDK prototype was available for me in Vala. |  | ||||||
|  |  | ||||||
| I had a `MatrixEvent` class that implemented the `GInitable` |  | ||||||
| interface, and many others were subclassed `MatrixEvent`. For some |  | ||||||
| reason I don’t remember, I created the following header for one of the |  | ||||||
| event classes: |  | ||||||
|  |  | ||||||
|     public class MatrixPresenceEvent : GLib.Object, GLib.Initable { |  | ||||||
|  |  | ||||||
| This is nice and everything, but as I didn’t create an `init()` method |  | ||||||
| for `MatrixPresenceEvent`, it tried to use the one from the parent |  | ||||||
| class and somehow got into an infinite loop. The Vala transformer |  | ||||||
| (`valac`), however, doesn’t mention this. |  | ||||||
|  |  | ||||||
| Lessons learned: if you implement an interface on a subclass that is |  | ||||||
| implemented by the parent don’t forget to add the necessary functions |  | ||||||
| to the subclass. |  | ||||||
| @@ -1,46 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Emacs package to generate GObject boilerplate" |  | ||||||
| date:      2016-09-28 15:40:15 |  | ||||||
| tags:      [gnome, development] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Before I started using Vala (and sometimes even after that) I often |  | ||||||
| needed to generate new classes based |  | ||||||
| on [GObject](https://developer.gnome.org/gobject/stable/). |  | ||||||
|  |  | ||||||
| If you have ever worked with GObject in C, you know how tedious it can |  | ||||||
| be. You need a pretty long boilerplate just to register your class, |  | ||||||
| and, if you want to be introspectable (and readable, actually), your |  | ||||||
| function names can grow really long. |  | ||||||
|  |  | ||||||
| To overcome this problem back in my ViM days, I used template files, |  | ||||||
| where I could replace class prefixes and names with a few keyboard |  | ||||||
| macros. As I never really dug into ViM scripting other than using some |  | ||||||
| plugins, I never got farther than |  | ||||||
| that. [Then came Emacs]({% post_url 2014-09-17-nyanmacs %}). |  | ||||||
|  |  | ||||||
| I use Emacs for about two years now very extensively, up to and |  | ||||||
| including GLib-based development. I tried the template approach, but |  | ||||||
| it felt to be a really poor experience, especially given that I made |  | ||||||
| my feet wet with Emacs Lisp. So I dug deeper, and created a package |  | ||||||
| for that. |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| GobGen has its own buffer with some widgets, a bit similar to |  | ||||||
| `customize`. You can enter the name of your new object and its parent, |  | ||||||
| specify some settings. Then you press Generate, and you are presented |  | ||||||
| with two new buffers, one for the `.c` and another for the `.h` |  | ||||||
| boilerplate. |  | ||||||
|  |  | ||||||
| There are a lot of things to do, actually. There is already an open |  | ||||||
| issue for creating a major mode for this buffer, and there are some |  | ||||||
| minor switches I’d like to add, but it is already usable. You can grab |  | ||||||
| it from [MELPA](https://melpa.org/#/gobgen) (my first package there; |  | ||||||
| woo!) or from |  | ||||||
| my [GitHub account](https://github.com/gergelypolonkai/gobgen.el). |  | ||||||
| @@ -1,65 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "git-merge stages" |  | ||||||
| date:      2016-10-04 12:46:00 |  | ||||||
| tags:      [git] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| This was a mail to my company’s internal Git mailing list, after I |  | ||||||
| realised many colleagues can’t wrap their heads around merge |  | ||||||
| conflicts. |  | ||||||
|  |  | ||||||
| >Hello all, |  | ||||||
| > |  | ||||||
| >I just saw this on |  | ||||||
| >the [git-users](https://groups.google.com/forum/#!forum/git-users) |  | ||||||
| >list and thought it could help you when you bump into a merge |  | ||||||
| >conflict. It is an excerpt from a mail by Konstantin Khomoutov (one |  | ||||||
| >of the main contributors on the list), with a few modifications of |  | ||||||
| >mine. Happy debugging :) |  | ||||||
| > |  | ||||||
| >>When a merge conflict is detected for a file, Git: |  | ||||||
| >> |  | ||||||
| >>1. Updates the entry for that file in the index to make it contain |  | ||||||
| >>   several so-called “stages”: |  | ||||||
| >>  * `0`: “Ours” version – that one which was there in this index entry |  | ||||||
| >>    before we begun to merge. At the beginning of the conflict, like |  | ||||||
| >>    right after the `git merge` or `git rebase` command this won’t |  | ||||||
| >>    exist (unless you had the file in the index, which you didn’t, did |  | ||||||
| >>    you?). When you resolve the conflict and use `git add |  | ||||||
| >>    my/conflicting/file.cc`, this will be the version added to the |  | ||||||
| >>    staging area (index), thus, the resolution of the conflict. |  | ||||||
| >>  * `1`: The version from the common ancestor commit, ie. the version |  | ||||||
| >>    of the file both of you modified. |  | ||||||
| >>  * `2`: The version from `HEAD`. During a merge, this is the current |  | ||||||
| >>    branch. During a rebase, this is the branch or commit you are |  | ||||||
| >>    rebasing onto, which usually will be `origin/develop`). |  | ||||||
| >>  * `3`: The version being merged, or the commit you are rebasing. |  | ||||||
| >>2. Updates the file in the work tree to contain conflict markers and |  | ||||||
| >>   the conflicting chunks of text between them (and the text from the |  | ||||||
| >>   common ancestor if the `diff3` style of conflict markers was set). |  | ||||||
| >> |  | ||||||
| >>Now you can use the numbers in point 1 to access the different stages |  | ||||||
| >>of the conflicting file. For example, to see the common ancestor (the |  | ||||||
| >>version both of you modified), use |  | ||||||
| >> |  | ||||||
| >>``` |  | ||||||
| >>git show :1:my/conflicting/file.cc |  | ||||||
| >>``` |  | ||||||
| >> |  | ||||||
| >>Or, to see the difference between the two conflicting versions, try |  | ||||||
| >> |  | ||||||
| >>``` |  | ||||||
| >>git diff :2:my/conflicting/file.cc :3:my/conflicting/file.cc |  | ||||||
| >>``` |  | ||||||
| >> |  | ||||||
| >>**Note** that you can’t use the `:0:` stage *before* you stage your |  | ||||||
| >>resolution with `git add`, and you can’t use the `:2:` and `:3:` |  | ||||||
| >>stages *after* you staged the resolution. |  | ||||||
| >> |  | ||||||
| >>Fun fact: behind the scenes, these are the files (*revisions*) `git mergetool` |  | ||||||
| >>accesses when it presents you the conflict visually. |  | ||||||
| @@ -1,98 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "How I started with Emacs" |  | ||||||
| date:      2016-11-03 09:58:41 |  | ||||||
| tags:      [emacs] |  | ||||||
| published: true |  | ||||||
| authon: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Sacha Chua has a nice |  | ||||||
| [Emacs chat intro](http://sachachua.com/blog/2013/04/emacs-chat-intro/) article |  | ||||||
| back from 2013. I write this post half because she asks there about my |  | ||||||
| (OK, anyone’s) first Emacs moments, and half because I plan to do it |  | ||||||
| for months now. |  | ||||||
|  |  | ||||||
| I wanted to start using Emacs 6(ish) years ago, and I was like |  | ||||||
| “<kbd>C-x</kbd> what”? (Note that back around 1998, I was among the |  | ||||||
| people who exited `vi` by killing it from another terminal after a |  | ||||||
| bunch of tries & fails like |  | ||||||
| [these](http://osxdaily.com/2014/06/12/how-to-quit-vim/).) |  | ||||||
|  |  | ||||||
| I tried to come back to Emacs a lot of times. And I mean a *lot*, |  | ||||||
| about every two months. I suddenly learned what these cryptic key |  | ||||||
| chord descriptions mean (`C` is for <kbd>Control</kbd> and `M` is for |  | ||||||
| <kbd>Meta</kbd>, which is actually <kbd>Alt</kbd>), but somehow it |  | ||||||
| didn’t *click*. I remained a ViM power user with a huge pile of |  | ||||||
| 3<sup>rd</sup> party plugins. |  | ||||||
| Then [I found Nyan-macs]({% post_url 2014-09-17-nyanmacs %}), |  | ||||||
| which converted me to Emacs, and it is final now. Many of my friends |  | ||||||
| thought I’m just kidding this being the cause, but I’m not. I’m a huge |  | ||||||
| fan of Nyan cat (did you know there is even a site |  | ||||||
| called [nyan.cat](http://nyan.cat/)?) and since then I have it in my |  | ||||||
| mode line: |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| …in my `eshell` prompt: |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| …and I also [zone out](https://www.emacswiki.org/emacs/ZoneMode) with |  | ||||||
| Nyan cat: |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Now on to more serious stuff. After browsing through all the packages |  | ||||||
| provided by [ELPA](http://elpa.gnu.org/), I found tons of useful (and |  | ||||||
| sometimes, less useful) packages, |  | ||||||
| like |  | ||||||
| [Helm](https://github.com/emacs-helm/helm/wiki), |  | ||||||
| [company](http://company-mode.github.io/), |  | ||||||
| [gtags](https://www.emacswiki.org/emacs/GnuGlobal) (which introduced |  | ||||||
| me to GNU Global, removing Exuberant ctags from my |  | ||||||
| life), |  | ||||||
| [magit](https://magit.vc/), |  | ||||||
| [Projectile](http://batsov.com/projectile/), |  | ||||||
| and [Org](http://orgmode.org/) (OK, it’s actually part of Emacs for a |  | ||||||
| while, but still). I still use these few, but in a month or two, I |  | ||||||
| started |  | ||||||
| to [version control](https://github.com/gergelypolonkai/my-emacs-d) my |  | ||||||
| `.emacs.d` directory, so I can easily transfer it between my home and |  | ||||||
| work machine (and for a few weeks now, even to my phone: I’m using |  | ||||||
| Termux on Android). Then, over these two years I wrote some packages |  | ||||||
| like [GobGen](https://github.com/gergelypolonkai/gobgen.el), and a |  | ||||||
| small addon for Calendar |  | ||||||
| providing |  | ||||||
| [Hungarian holidays](https://github.com/gergelypolonkai/hungarian-holidays), |  | ||||||
| and I found a lot more (in no particular |  | ||||||
| order): |  | ||||||
| [git-gutter](https://github.com/syohex/emacs-git-gutter), |  | ||||||
| [multiple-cursors](https://github.com/magnars/multiple-cursors.el), |  | ||||||
| [origami](https://github.com/gregsexton/origami.el), |  | ||||||
| [ace-window](https://github.com/abo-abo/ace-window), |  | ||||||
| [avy](https://github.com/abo-abo/avy), |  | ||||||
| [beacon](https://github.com/Malabarba/beacon), and a lot more. |  | ||||||
|  |  | ||||||
| What is more important (to me) is that I started using |  | ||||||
| the [use-package](https://github.com/jwiegley/use-package) package, |  | ||||||
| which can automatically download packages that are not installed on my |  | ||||||
| current local system. Together |  | ||||||
| with |  | ||||||
| [auto-package-update](https://github.com/rranelli/auto-package-update.el), |  | ||||||
| it is *very* practical. |  | ||||||
|  |  | ||||||
| In addition, I started to follow the blogs of a bunch of Emacs |  | ||||||
| users/gurus. I’ve already |  | ||||||
| mentioned [Sacha Chua](http://sachachua.com/). She’s a charming, |  | ||||||
| cheerful person, writing a lot about Emacs and project management |  | ||||||
| (among other things). Another one |  | ||||||
| is [Bozhidar Batsov](http://batsov.com/), who, among other things, had |  | ||||||
| an initiate to lay down the foundation of |  | ||||||
| a |  | ||||||
| [common Elisp coding style](https://github.com/bbatsov/emacs-lisp-style-guide). Another |  | ||||||
| favourite of mine |  | ||||||
| is [Endless Parentheses](http://endlessparentheses.com/), whence I got |  | ||||||
| a lot of ideas. |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Edit file as another user in Emacs" |  | ||||||
| date:      2016-11-10 08:57:12 |  | ||||||
| tags:      [development, emacs] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I have recently found |  | ||||||
| [this article](http://emacsredux.com/blog/2013/04/21/edit-files-as-root/) by |  | ||||||
| Bozhidar Batsov on opening the current file as root. I barely use |  | ||||||
| [tramp](https://www.gnu.org/software/tramp/) for sudo access, but when I do, |  | ||||||
| I almost never use root as the target user. So I decided to fix it for my |  | ||||||
| needs. |  | ||||||
|  |  | ||||||
| {% gist 192c83aa0556d5cdaf4018f57b75a84b %} |  | ||||||
|  |  | ||||||
| If the user is not specified, the default is still root. Also, if the |  | ||||||
| current buffer is not visiting a file, I prompt for a filename. As I’m not |  | ||||||
| an `ido` user, I didn’t bother calling |  | ||||||
| `ido-read-file-name`; [`helm`](https://github.com/emacs-helm/helm/wiki) |  | ||||||
| overrides `read-file-name` for me anyway. |  | ||||||
|  |  | ||||||
| Unlike Bozhidar, I barely use this feature, so I didn’t bind this to a key. |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Get account data programatically from id-manager" |  | ||||||
| date:      2016-11-18 12:43:13 |  | ||||||
| tags:      [emacs] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I recently started |  | ||||||
| using [`id-manager`](https://github.com/kiwanami/emacs-id-manager). It is a |  | ||||||
| nice little package that can store your passwords, encrypting them with |  | ||||||
| GPG. My original reason was to store my GitHub access token |  | ||||||
| for [`github-notifier`](https://github.com/xuchunyang/github-notifier.el), |  | ||||||
| but it soon turned out, it’s not *that* easy. |  | ||||||
|  |  | ||||||
| `id-manager` is a nice package when it comes to storing your password, and |  | ||||||
| retrieving them for your own eyes. But it cannot retrieve account data |  | ||||||
| programatically. Taking a look into its source code, I came up with this |  | ||||||
| solution: |  | ||||||
|  |  | ||||||
| {% gist 8bad70502ac563864080f754fce726c3 idm.el %} |  | ||||||
|  |  | ||||||
| I currently need only the account ID (ie. the username) and the password, |  | ||||||
| but it’s pretty easy to add a macro to get the `memo` or `update-time` |  | ||||||
| fields, too. |  | ||||||
| @@ -1,47 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    "post" |  | ||||||
| title:     "Add Python docstring to the beginning of anything in Emacs" |  | ||||||
| date:      2016-11-30 07:52:37 |  | ||||||
| tags:      [development, python, emacs] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Now that I write Python code for a living, I write a lot of functions, |  | ||||||
| classes, and modules.  What I still tend to forget, and also find tedious, |  | ||||||
| is adding docstrings.  Unlike many developers, writing documentation is not |  | ||||||
| an enemy of mine, but it usually comes to my mind when I finish |  | ||||||
| implementation.  The procedure, roughly, is this: |  | ||||||
|  |  | ||||||
| * Decide where I am (in a function, in a class but not in one of its |  | ||||||
|   methods, or not inside such a block at all) |  | ||||||
| * Go to the beginning of the thing |  | ||||||
| * Insert `"""` |  | ||||||
| * Leave a blank line |  | ||||||
| * Insert `"""` |  | ||||||
|  |  | ||||||
| One of my mottos is if something takes more than one step and you have to do |  | ||||||
| it more than twice, you should automate it after the first time.  This puts |  | ||||||
| a small(ish) overhead on the second invocation (when you implement the |  | ||||||
| automation), but it usually worth the time. |  | ||||||
|  |  | ||||||
| Since I use Emacs for pretty much everything coding-related (and many more, |  | ||||||
| but that’s the topic of a different post), I wrote a small function to do it |  | ||||||
| for me. |  | ||||||
|  |  | ||||||
| {% gist 7b062a00d3b8a2555024521273cecfee python-docstring.el %} |  | ||||||
|  |  | ||||||
| There are still a lot of things to improve: |  | ||||||
|  |  | ||||||
| * it always inserts double quotes (althoug I couldn’t show a use-case when |  | ||||||
| single quotes are preferred) |  | ||||||
| * it doesn’t check for an existing docstring, just happily inserts a new one |  | ||||||
| (leaving the old one alone, but generating a syntax error this way) |  | ||||||
| * it would also be nice if I could jump to the beginning of a file even from |  | ||||||
| a class method.  I guess I will use prefix keys for that, but I’m not sure |  | ||||||
| yet. |  | ||||||
|  |  | ||||||
| You can bet I will implement these features, so check back soon for an |  | ||||||
| updated version! |  | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Slugify in Python 3" |  | ||||||
| date:      2016-12-08 12:54:19 |  | ||||||
| tags:      [development, python] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| Today I needed a function to create a slug (an ASCII-only representation of |  | ||||||
| a string).  I went Googling a bit, and found an |  | ||||||
| excellend [Flask snippet](http://flask.pocoo.org/snippets/5/).  Problem is, |  | ||||||
| it is designed for Python 2, so I came up with a Python 3 version. |  | ||||||
|  |  | ||||||
| {% gist 1866fd363f75f4da5f86103952e387f6 slugify.py %} |  | ||||||
|  |  | ||||||
| As I don’t really like the transliteration done in the first example |  | ||||||
| (e.g. converting ü to ue), I went with the second example. |  | ||||||
| @@ -1,115 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Finding non-translated strings in Python code" |  | ||||||
| date:      2016-12-22 09:35:11 |  | ||||||
| tags:      [development, python] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| When creating multilingual software, be it on the web, mobile, or desktop, |  | ||||||
| you will eventually fail to mark strings as translatable.  I know, I know, |  | ||||||
| we developers are superhuman and never do that, but somehow I stopped |  | ||||||
| trusting myself recently, so I came up with an idea. |  | ||||||
|  |  | ||||||
| Right now I assist in the creation of a multilingual site/web application, |  | ||||||
| where a small part of the strings come from the Python code instead of HTML |  | ||||||
| templates.  Call it bad practice if you like, but I could not find a better |  | ||||||
| way yet. |  | ||||||
|  |  | ||||||
| As a start, I tried to parse the source files with simple regular |  | ||||||
| expressions, so I could find anything between quotation marks or |  | ||||||
| apostrophes.  This attempt quickly failed with strings that had such |  | ||||||
| characters inside, escaped or not; my regexps became so complex I lost all |  | ||||||
| hope.  Then the magic word “lexer” came to mind. |  | ||||||
|  |  | ||||||
| While searching for ready made Python lexers, I bumped into the awesome |  | ||||||
| `ast` module.  AST stands for Abstract Syntax Tree, and this module does |  | ||||||
| that: parses a Python file and returns a tree of nodes.  For walking through |  | ||||||
| these nodes there is a `NodeVisitor` class (among other means), which is |  | ||||||
| meant to be subclassed.  You add a bunch of `visitN` methods (where `N` is |  | ||||||
| an `ast` class name like `Str` or `Call`), instantiate it, and call its |  | ||||||
| `visit()` method with the root node.  For example, the `visitStr()` method |  | ||||||
| will be invoked for every string it finds. |  | ||||||
|  |  | ||||||
| #### How does it work? |  | ||||||
|  |  | ||||||
| Before getting into the details, let’s me present you the code I made: |  | ||||||
|  |  | ||||||
| {% gist 1a16a47e5a1971ca33e58bdfd88c5059 string-checker.py %} |  | ||||||
|  |  | ||||||
| The class initialization does two things: creates an empty `in_call` list |  | ||||||
| (this will hold our primitive backtrace), and saves the filename, if |  | ||||||
| provided. |  | ||||||
|  |  | ||||||
| `visitCall`, again, has two tasks.  First, it checks if we are inside a |  | ||||||
| translation function.  If so, it reports the fact that we are translating |  | ||||||
| something that is not a raw string.  Although it is not necessarily a bad |  | ||||||
| thing, I consider it bad practice as it may result in undefined behaviour. |  | ||||||
|  |  | ||||||
| Its second task is to walk through the positional and keyword arguments of |  | ||||||
| the function call.  For each argument it calls the `visit_with_trace()` |  | ||||||
| method. |  | ||||||
|  |  | ||||||
| This method updates the `in_call` property with the current function name |  | ||||||
| and the position of the call.  This latter is needed because `ast` doesn’t |  | ||||||
| store position information for every node (operators are a notable example). |  | ||||||
| Then it simply visits the argument node, which is needed because |  | ||||||
| `NodeVisitor.visit()` is not recursive.  When the visit is done (which, with |  | ||||||
| really deeply nested calls like `visit(this(call(iff(you(dare)))))` will be |  | ||||||
| recursive), the current function name is removed from `in_call`, so |  | ||||||
| subsequent calls on the same level see the same “backtrace”. |  | ||||||
|  |  | ||||||
| The `generic_visit()` method is called for every node that doesn’t have a |  | ||||||
| named visitor (like `visitCall` or `visitStr`.  For the same reason we |  | ||||||
| generate a warning in `visitCall`, we do the same here.  If there is |  | ||||||
| anything but a raw string inside a translation function call, developers |  | ||||||
| should know about it. |  | ||||||
|  |  | ||||||
| The last and I think the most important method is `visitStr`.  All it does |  | ||||||
| is checking the last element of the `in_call` list, and generates a warning |  | ||||||
| if a raw string is found somewhere that is not inside a translation function |  | ||||||
| call. |  | ||||||
|  |  | ||||||
| For accurate reports, there is a `get_func_name()` function that takes an |  | ||||||
| `ast` node as an argument.  As function call can be anything from actual |  | ||||||
| functions to object methods, this goes all down the node’s properties, and |  | ||||||
| recursively reconstructs the name of the actual function. |  | ||||||
|  |  | ||||||
| Finally, there are some test functions in this code.  `tst` and |  | ||||||
| `actual_tests` are there so if I run a self-check on this script, it will |  | ||||||
| find these strings and report all the untranslated strings and all the |  | ||||||
| potential problems like the string concatenation. |  | ||||||
|  |  | ||||||
| #### Drawbacks |  | ||||||
|  |  | ||||||
| There are several drawbacks here.  First, translation function names are |  | ||||||
| built in, to the `TRANSLATION_FUNCTIONS` property of the `ShowString` class. |  | ||||||
| You must change this if you use other translation functions like |  | ||||||
| `dngettext`, or if you use a translation library other than `gettext`. |  | ||||||
|  |  | ||||||
| Second, it cannot ignore untranslated strings right now.  It would be great |  | ||||||
| if a pragma like `flake8`’s `# noqa` or `coverage.py`’s `# pragma: no cover` |  | ||||||
| could be added.  However, `ast` doesn’t parse comment blocks, so this proves |  | ||||||
| to be challenging. |  | ||||||
|  |  | ||||||
| Third, it reports docstrings as untranslated.  Clearly, this is wrong, as |  | ||||||
| docstrings generally don’t have to be translated.  Ignoring them, again, is |  | ||||||
| a nice challenge I couldn’t yet overcome. |  | ||||||
|  |  | ||||||
| The `get_func_name()` helper is everything but done.  As long as I cannot |  | ||||||
| remove that final `else` clause, there may be error reports.  If that |  | ||||||
| happens, the reported class should be treated in a new `elif` branch. |  | ||||||
|  |  | ||||||
| Finally (and the most easily fixed), the warnings are simply printed on the |  | ||||||
| console.  It is nice, but it should be optional; the problems identified |  | ||||||
| should be stored so the caller can obtain it as an array. |  | ||||||
|  |  | ||||||
| #### Bottom line |  | ||||||
|  |  | ||||||
| Finding strings in Python sources is not as hard as I imagined.  It was fun |  | ||||||
| to learn using the `ast` module, and it does a great job.  Once I can |  | ||||||
| overcome the drawbacks above, this script will be a fantastic piece of code |  | ||||||
| that can assist me in my future tasks. |  | ||||||
| @@ -1,34 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Rename automatically named foreign keys with Alembic" |  | ||||||
| date:      2017-01-02 09:41:23 |  | ||||||
| tags:      [mysql, development, flask, python] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I have recently messed up my Alembic migrations while modifying my |  | ||||||
| SQLAlchemy models.  To start with, I didn’t update the auto-generated |  | ||||||
| migration files to name the indexes/foreign keys a name, so Alembic used its |  | ||||||
| own naming scheme.  This is not an actual problem until you have to modify |  | ||||||
| columns that have such constraints.  I have since fixed this problem, but |  | ||||||
| first I had to find which column references what (I had no indexes other |  | ||||||
| than primary key back then, so I could go with foreign keys only).  Here is |  | ||||||
| a query I put together, mostly using |  | ||||||
| [this article](http://www.binarytides.com/list-foreign-keys-in-mysql/). |  | ||||||
|  |  | ||||||
| ``` sql |  | ||||||
| SELECT constraint_name, |  | ||||||
|        CONCAT(table_name, '.', column_name) AS 'foreign key', |  | ||||||
|        CONCAT(referenced_table_name, '.', referenced_column_name) AS 'references' |  | ||||||
| FROM information_schema.key_column_usage |  | ||||||
| WHERE referenced_table_name IS NOT NULL AND |  | ||||||
|       table_schema = 'my_app'; |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Now I could easily drop such constraints using |  | ||||||
| `alembic.op.drop_constraint('users_ibfk1', 'users', type_='foreignkey')` and |  | ||||||
| recreate them with `alembic.op.create_foreign_key('fk_user_client', 'users', |  | ||||||
| 'clients', ['client_id'], ['id'])` |  | ||||||
| @@ -1,91 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Category-based logging with Flask" |  | ||||||
| date:      2017-03-26 22:00:52 |  | ||||||
| tags:      [development, python, flask] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I’m in a team who are developing a Flask-based web application, which uses |  | ||||||
| logging extensively.  For a while now it spews out a lot of lines so the |  | ||||||
| need arose to index them in ElasticSearch, and more importantly, to search |  | ||||||
| through them for auditing purposes.  This latter user story brought up one |  | ||||||
| more question: why don’t we categorize our log messages?  I quickly came up |  | ||||||
| with an extended log format (`[auth]` is the new category name): |  | ||||||
|  |  | ||||||
|     [2017-01-14 00:55:42,554] [8286] [INFO] [auth] invalid password for john@example.com [at __init__.py:12, in function utils.validate_login] |  | ||||||
|  |  | ||||||
| Here, `[auth]` is the category name.  In the ideal solution, all I’d have to |  | ||||||
| do is adding `%(category)s` to my formatter, and I could call |  | ||||||
| `app.logger.info('auth', 'invalid password')` to achieve this output. |  | ||||||
| Unfortunately, `Flask.logger` (and, in the background, the `logging` module) |  | ||||||
| is not that easy to tame. |  | ||||||
|  |  | ||||||
| As it turns out, a Flask application’s `logger` property is a subclass of |  | ||||||
| `logging.Logger`, so my first idea was to monkey patch that class.  When the |  | ||||||
| app’s logger is initialised, it subclasses `logging.Logger` and tweaks the |  | ||||||
| log level so it goes down to `logging.DEBUG` if the app is running in debug |  | ||||||
| mode.  This is done by using a different logger class depending on the app |  | ||||||
| config.  Fortunately it doesn’t directly subclass `logging.Logger`; it calls |  | ||||||
| `logging.getLoggerClass()` to find which class it should extend.  To achieve |  | ||||||
| my goals, all I had to do is to subclass the original logger class, and pass |  | ||||||
| it to `logging.setLoggerClass()` *before* I initialise my app, and I have a |  | ||||||
| fail-safe(ish) solution.  So far so good, on to the extra category |  | ||||||
| parameter. |  | ||||||
|  |  | ||||||
| Now if you add a new variable to the formatter like my new `%(category)s`, |  | ||||||
| you get a nifty `KeyError` saying there is no `category` in the format |  | ||||||
| expansion dictionary.  If you add `category='auth` to the |  | ||||||
| `app.logger.info()` calls and its cousins, it’s fine, because these methods |  | ||||||
| use the magic `**kwarg` argument to swallow it. Everything goes well until |  | ||||||
| control arrives to the `_log()` method: it complains about that extra |  | ||||||
| `category` keyword argument.  Taking a peek at Python’s internals, I found |  | ||||||
| two things: `info()`, `error()`, and co. pass `*args` and `**kwargs` to |  | ||||||
| `_log()` unmodified, and the `_log()` method doesn’t have `**kwargs` |  | ||||||
| present in its definition to swallow it.  A little doc reading later I found |  | ||||||
| that if I want to pass extra arguments for such a formatter, I should do it |  | ||||||
| via the `extra` keyword argument to `_log()`.  A call like |  | ||||||
| `app.logger.info('invalid password', extra={'category': 'auth'})` solved the |  | ||||||
| problem.  Now *that* is tedious. |  | ||||||
|  |  | ||||||
| My first idea was to override all the standard logging methods like `info()` |  | ||||||
| and `error()`, and handle `category` there.  But this resulted in lots of |  | ||||||
| repeating code.  I changed the specification a bit, so my calls would look |  | ||||||
| like `info('message', category='auth)` instead of the original plan of |  | ||||||
| `info('auth', 'message')`: as the logging methods pass all keyword arguments |  | ||||||
| to `_log()`, I can handle it there.  So at the end, my new logger class |  | ||||||
| only patches `_log()`, by picking out `category` from the kwarg list, and |  | ||||||
| inserting it to `extra` before calling `super`. |  | ||||||
|  |  | ||||||
| As you can see, this is a bit ugly solution.  It requires me, the app |  | ||||||
| author, to know about Flask’s internals (that I can set my own logging class |  | ||||||
| before the app is created, and so the app will use it.)  This means if Flask |  | ||||||
| developers change the way how logging is done, I have to adapt and find a |  | ||||||
| workaround for the new version (well, unless they let me directly set the |  | ||||||
| logging class.  That would make it easy.) |  | ||||||
|  |  | ||||||
| What is worse, I must know about Python internals.  I know the `extra` kwarg |  | ||||||
| is documented well (I just failed to notice), but this made adding a new |  | ||||||
| formatter variable so hard.  Python version doesn’t change as often as Flask |  | ||||||
| version in this project, and I think the standard library won’t really |  | ||||||
| change until 4.0, so I don’t think my tampering with a “protected” method |  | ||||||
| will cause any trouble in the future.  Still, this makes me feel a bit |  | ||||||
| uneasy. |  | ||||||
|  |  | ||||||
| All the above can be circumvented if this class, and the whole solution have |  | ||||||
| some tests.  As my class uses the same method as Flask (use |  | ||||||
| `logging.getLoggerClass()` as a base class instead of using |  | ||||||
| `logging.Logger()` directly), if the base logger class changes in Python or |  | ||||||
| in the running environment, my app won’t care.  By checking if the app |  | ||||||
| logger can use my special `category` variable (ie. it doesn’t raise an |  | ||||||
| exception *and* the category actually gets into the log output), I made sure |  | ||||||
| my class is used as a base in Flask, so if they change the way they |  | ||||||
| construct `app.logger`, I will know about it when I first run my tests after |  | ||||||
| upgrading Flask. |  | ||||||
|  |  | ||||||
| If you are interested in such functionality (and more), you can grab it |  | ||||||
| from [GitHub](https://github.com/gergelypolonkai/flask-logging-extras), or |  | ||||||
| via [PyPI](https://pypi.python.org/pypi/Flask-Logging-Extras/). |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Add SysAdmin day to Emacs Calendar" |  | ||||||
| date:      2017-10-02 09:37:52 |  | ||||||
| tags:      [emacs] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
| I’m a SysAdmin since 1998.  Maybe a bit earlier, if you count managing our home computer.  This |  | ||||||
| means [SysAdmin Day](http://sysadminday.com/) is also celebrating me.  However, my Emacs Calendar |  | ||||||
| doesn’t show it for some reason. |  | ||||||
|  |  | ||||||
| The solution is pretty easy: |  | ||||||
|  |  | ||||||
| ``` lisp |  | ||||||
| (add-to-list 'holiday-other-holidays '(holiday-float 7 5 -1 "SysAdmin Day") t) |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Now invoke `holidays-list` for any year, choosing “Other” as the category, and there you go: |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| … |  | ||||||
| Friday, July 28, 2017: SysAdmin Day |  | ||||||
| … |  | ||||||
| ``` |  | ||||||
| @@ -1,214 +0,0 @@ | |||||||
| --- |  | ||||||
| layout:    post |  | ||||||
| title:     "Check if the last Git commit has test coverage" |  | ||||||
| date:      2018-07-26 12:49:52 |  | ||||||
| tags:      [python,development,testing] |  | ||||||
| published: true |  | ||||||
| author: |  | ||||||
|     name: Gergely Polonkai |  | ||||||
|     email: gergely@polonkai.eu |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| I use Python at work and for private projects.  I also aim to write tests for my code, especially |  | ||||||
| recently.  And as I usually don’t start from 100% code coverage (TDD is not my game), I at least |  | ||||||
| want to know if the code I just wrote have full coverage. |  | ||||||
|  |  | ||||||
| The trick is to collect all the lines that changed, and all the lines that has no coverage.  Then |  | ||||||
| compare the two, and you have the uncovered lines that changed! |  | ||||||
|  |  | ||||||
| ### Getting the list of changed lines |  | ||||||
|  |  | ||||||
| Recently, I bumped into |  | ||||||
| [this article](https://adam.younglogic.com/2018/07/testing-patch-has-test/).  It is a great awk |  | ||||||
| script that lists the lines that changed in the latest commit.  I have really no problem with awk, |  | ||||||
| but I’m pretty sure it can be done in Python, as that is my main language nowadays. |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| def get_changed_lines(): |  | ||||||
|     """Get the line numbers that changed in the last commit |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     git_output = subprocess.check_output('git show', shell=True).decode('utf-8') |  | ||||||
|  |  | ||||||
|     current_file = None |  | ||||||
|     lines = {} |  | ||||||
|     left = 0 |  | ||||||
|     right = 0 |  | ||||||
|  |  | ||||||
|     for line in git_output.split('\n'): |  | ||||||
|         match = re.match(r'^@@ -([0-9]+),[0-9]+ [+]([0-9]+),[0-9]+ @@', line) |  | ||||||
|  |  | ||||||
|         if match: |  | ||||||
|             left = int(match.groups()[0]) |  | ||||||
|             right = int(match.groups()[1]) |  | ||||||
|  |  | ||||||
|             continue |  | ||||||
|  |  | ||||||
|         if re.match(r'^\+\+\+', line): |  | ||||||
|             current_file = line[6:] |  | ||||||
|  |  | ||||||
|             continue |  | ||||||
|  |  | ||||||
|         if re.match(r'^-', line): |  | ||||||
|             left += 1 |  | ||||||
|  |  | ||||||
|             continue |  | ||||||
|  |  | ||||||
|         if re.match(r'^[+]', line): |  | ||||||
|             # Save this line number as changed |  | ||||||
|             lines.setdefault(current_file, []) |  | ||||||
|             lines[current_file].append(right) |  | ||||||
|             right += 1 |  | ||||||
|  |  | ||||||
|             continue |  | ||||||
|  |  | ||||||
|         left += 1 |  | ||||||
|         right += 1 |  | ||||||
|  |  | ||||||
|     return lines |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| OK, not as short as the awk script, but works just fine. |  | ||||||
|  |  | ||||||
| ### Getting the uncovered lines |  | ||||||
|  |  | ||||||
| Coverage.py can list the uncovered lines with `coverage report --show-missing`.  For Calendar.social, this looks something like this: |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| Name                                     Stmts   Miss  Cover   Missing |  | ||||||
| ---------------------------------------------------------------------- |  | ||||||
| calsocial/__init__.py                      173     62    64%   44, 138-148, 200, 239-253, 261-280, 288-295, 308-309, 324-346, 354-363 |  | ||||||
| calsocial/__main__.py                        3      3     0%   4-9 |  | ||||||
| calsocial/account.py                       108     51    53%   85-97, 105-112, 125, 130-137, 148-160, 169-175, 184-200, 209-212, 221-234 |  | ||||||
| calsocial/app_state.py                      10      0   100% |  | ||||||
| calsocial/cache.py                          73     11    85%   65-70, 98, 113, 124, 137, 156-159 |  | ||||||
| calsocial/calendar_system/__init__.py       10      3    70%   32, 41, 48 |  | ||||||
| calsocial/calendar_system/gregorian.py      77      0   100% |  | ||||||
| calsocial/config_development.py             11     11     0%   4-17 |  | ||||||
| calsocial/config_testing.py                 12      0   100% |  | ||||||
| calsocial/forms.py                         198     83    58%   49, 59, 90, 136-146, 153, 161-169, 188-195, 198-206, 209-212, 228-232, 238-244, 252-253, 263-267, 273-277, 317-336, 339-342, 352-354, 362-374, 401-413 |  | ||||||
| calsocial/models.py                        372     92    75%   49-51, 103-106, 177, 180-188, 191-200, 203, 242-248, 257-268, 289, 307, 349, 352-359, 378, 392, 404-409, 416, 444, 447, 492-496, 503, 510, 516, 522, 525, 528, 535-537, 545-551, 572, 606-617, 620, 652, 655, 660, 700, 746-748, 762-767, 774-783, 899, 929, 932 |  | ||||||
| calsocial/security.py                       15      3    80%   36, 56-58 |  | ||||||
| calsocial/utils.py                          42      5    88%   45-48, 52-53 |  | ||||||
| ---------------------------------------------------------------------- |  | ||||||
| TOTAL                                     1104    324    71% |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| All we have to do is converting these ranges into a list of numbers, and compare it with the |  | ||||||
| result of the previous function: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| def get_uncovered_lines(changed_lines): |  | ||||||
|     """Get the full list of lines that has not been covered by tests |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     column_widths = [] |  | ||||||
|     uncovered_lines = {} |  | ||||||
|  |  | ||||||
|     for line in sys.stdin: |  | ||||||
|         line = line.strip() |  | ||||||
|  |  | ||||||
|         if line.startswith('---'): |  | ||||||
|             continue |  | ||||||
|  |  | ||||||
|         if line.startswith('Name '): |  | ||||||
|             match = re.match(r'^(Name +)(Stmts +)(Miss +)(Cover +)Missing$', line) |  | ||||||
|             assert match |  | ||||||
|  |  | ||||||
|             column_widths = [len(col) for col in match.groups()] |  | ||||||
|  |  | ||||||
|             continue |  | ||||||
|  |  | ||||||
|         name = [ |  | ||||||
|             line[sum(column_widths[0:idx]):sum(column_widths[0:idx]) + width].strip() |  | ||||||
|             for idx, width in enumerate(column_widths)][0] |  | ||||||
|         missing = line[sum(column_widths):].strip() |  | ||||||
|  |  | ||||||
|         for value in missing.split(', '): |  | ||||||
|             if not value: |  | ||||||
|                 continue |  | ||||||
|  |  | ||||||
|             try: |  | ||||||
|                 number = int(value) |  | ||||||
|             except ValueError: |  | ||||||
|                 first, last = value.split('-') |  | ||||||
|                 lines = range(int(first), int(last) + 1) |  | ||||||
|             else: |  | ||||||
|                 lines = range(number, number + 1) |  | ||||||
|  |  | ||||||
|             for lineno in lines: |  | ||||||
|                 if name in changed_lines and lineno not in changed_lines[name]: |  | ||||||
|                     uncovered_lines.setdefault(name, []) |  | ||||||
|                     uncovered_lines[name].append(lineno) |  | ||||||
|  |  | ||||||
|     return uncovered_lines |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| At the end we have a dictionary that has filenames as keys, and a list of changed but uncovered |  | ||||||
| lines. |  | ||||||
|  |  | ||||||
| ### Converting back to ranges |  | ||||||
|  |  | ||||||
| To make the final result more readable, let’s convert them back to a nice `from_line-to_line` |  | ||||||
| range list first: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| def line_numbers_to_ranges(): |  | ||||||
|     """List the lines that has not been covered |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     changed_lines = get_changed_lines() |  | ||||||
|     uncovered_lines = get_uncovered_lines(changed_lines) |  | ||||||
|  |  | ||||||
|     line_list = [] |  | ||||||
|  |  | ||||||
|     for filename, lines in uncovered_lines.items(): |  | ||||||
|         lines = sorted(lines) |  | ||||||
|         last_value = None |  | ||||||
|  |  | ||||||
|         ranges = [] |  | ||||||
|  |  | ||||||
|         for lineno in lines: |  | ||||||
|             if last_value and last_value + 1 == lineno: |  | ||||||
|                 ranges[-1].append(lineno) |  | ||||||
|             else: |  | ||||||
|                 ranges.append([lineno]) |  | ||||||
|  |  | ||||||
|             last_value = lineno |  | ||||||
|  |  | ||||||
|         range_list = [] |  | ||||||
|  |  | ||||||
|         for range_ in ranges: |  | ||||||
|             first = range_.pop(0) |  | ||||||
|  |  | ||||||
|             if range_: |  | ||||||
|                 range_list.append(f'{first}-{range_[-1]}') |  | ||||||
|             else: |  | ||||||
|                 range_list.append(str(first)) |  | ||||||
|  |  | ||||||
|         line_list.append((filename, ', '.join(range_list))) |  | ||||||
|  |  | ||||||
|     return line_list |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### Printing the result |  | ||||||
|  |  | ||||||
| Now all that is left is to print the result on the screen in a format digestable by a human being: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| def tabular_print(uncovered_lines): |  | ||||||
|     """Print the list of uncovered lines on the screen in a tabbed format |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     max_filename_len = max(len(data[0]) for data in uncovered_lines) |  | ||||||
|  |  | ||||||
|     for filename, lines in uncovered_lines: |  | ||||||
|         print(filename.ljust(max_filename_len + 2) + lines) |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| And we are done. |  | ||||||
|  |  | ||||||
| ### Conclusion |  | ||||||
|  |  | ||||||
| This task never seemed hard to accomplish, but somehow I never put enough energy into it to make |  | ||||||
| it happen.  Kudos to Adam Young doing some legwork for me! |  | ||||||
							
								
								
									
										24
									
								
								about.html
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								about.html
									
									
									
									
									
								
							| @@ -1,24 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: page |  | ||||||
| title: About the author |  | ||||||
| permalink: /about/ |  | ||||||
| --- |  | ||||||
| {% include about.html %} |  | ||||||
|  |  | ||||||
| <h3>Some fun stats and links</h3> |  | ||||||
|  |  | ||||||
| <h4>I’m a registered Linux User :)</h4> |  | ||||||
| <p> |  | ||||||
|     <a href="https://www.linuxcounter.net/user/416972"> |  | ||||||
|         <img src="https://www.linuxcounter.net/cert/416972.png" |  | ||||||
|              alt="I am Linux user #416972"> |  | ||||||
|     </a> |  | ||||||
| </p> |  | ||||||
|  |  | ||||||
| <h4>I have my own GeekCode</h4> |  | ||||||
| <pre> |  | ||||||
| -----BEGIN GEEK CODE BLOCK----- |  | ||||||
| Version: 3.1 |  | ||||||
| GCM/CS/IT/O d--(+) s++:- a C++$ UB++L++++$ P++ L+++$ E++>$ W+++$ N o? K w+ O M- V PS+ PE Y+ PGP+(++) t+ 5 X R !tv b+ DI++ D+ G e h----() r+++ y++++ |  | ||||||
| -----END GEEK CODE BLOCK----- |  | ||||||
| </pre> |  | ||||||
| @@ -1,30 +0,0 @@ | |||||||
| --- |  | ||||||
| --- |  | ||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> |  | ||||||
|     <channel> |  | ||||||
|         <title>{{site.title | xml_escape}}</title> |  | ||||||
|         <description>{{site.description | xml_escape}}</description> |  | ||||||
|         <link>{{site.baseurl | prepend: site.url}}</link> |  | ||||||
|         <atom:link href="{{'/blog/atom.xml' | prepend: site.baseurl | prepend: site.url}}" rel="self" type="application/rss+xml" /> |  | ||||||
|         <lastBuildDate>{{site.posts.first.date | date: "%a, %d %b %Y %H:%M:%S %z"}}</lastBuildDate> |  | ||||||
|         <pubDate>{{site.posts.first.date | date: "%a, %d %b %Y %H:%M:%S %z"}}</pubDate> |  | ||||||
|         <ttl>7200</ttl> |  | ||||||
|         <language>en</language> |  | ||||||
|         <image> |  | ||||||
|             <title>Gergely Polonkai</title> |  | ||||||
|             <url>{{'/images/profile.png' | prepend: site.baseurl | prepend: site.url}}</url> |  | ||||||
|             <link>{{'/' | prepend: site.baseurl | prepend: site.url}}</link> |  | ||||||
|         </image> |  | ||||||
| {% for post in site.posts limit:10 %} |  | ||||||
|         <item> |  | ||||||
|             <title>{{post.title | xml_escape}}</title> |  | ||||||
|             <link>{{post.url | prepend: site.baseurl | prepend: site.url}}</link> |  | ||||||
|             <comments>{{post.url | prepend: site.baseurl | prepend: site.url}}#comments</comments> |  | ||||||
|             <pubDate>{{post.date | date: "%a, %d %b %Y %H:%M:%S %z"}}</pubDate> |  | ||||||
|             <description>{{post.excerpt | xml_escape}}</description> |  | ||||||
|             <guid isPermaLink="true">{{post.url | prepend: site.baseurl | prepend: site.url}}</guid> |  | ||||||
|         </item> |  | ||||||
| {% endfor %} |  | ||||||
|     </channel> |  | ||||||
| </rss> |  | ||||||
| @@ -1,12 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: page |  | ||||||
| title: Blog posts |  | ||||||
| post_listing: true |  | ||||||
| --- |  | ||||||
| {% include pagination.html %} |  | ||||||
|  |  | ||||||
| {% assign posts = paginator.posts %} |  | ||||||
|  |  | ||||||
| {% include post-list.html %} |  | ||||||
|  |  | ||||||
| {% include pagination.html %} |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: posts-by-tag |  | ||||||
| tag:    active-directory |  | ||||||
| --- |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: posts-by-tag |  | ||||||
| tag:    apache |  | ||||||
| --- |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: posts-by-tag |  | ||||||
| tag:    astrology |  | ||||||
| --- |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: posts-by-tag |  | ||||||
| tag:    c |  | ||||||
| --- |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: posts-by-tag |  | ||||||
| tag:    citrix-xenserver |  | ||||||
| --- |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: posts-by-tag |  | ||||||
| tag:    command-line |  | ||||||
| --- |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: posts-by-tag |  | ||||||
| tag:    conference |  | ||||||
| --- |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: posts-by-tag |  | ||||||
| tag:    css |  | ||||||
| --- |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: posts-by-tag |  | ||||||
| tag:    debian |  | ||||||
| --- |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| --- |  | ||||||
| layout: posts-by-tag |  | ||||||
| tag:    development |  | ||||||
| --- |  | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user