Quantcast
Channel: Planeta Código
Viewing all 2731 articles
Browse latest View live

Blog Bitix: Películas sobre tecnología o informática, series, documentales, vídeos, libros, GNU/Linux y software libre

$
0
0

Si estás interesado en la tecnología, informática en general y en el software libre, código abierto y GNU/Linux estoy más que seguro que las siguientes listas de películas, series, documentales y libros te gustarán bastante. Probablemente algunas ya hayas visto pero otra quizá no conocías o aún no has visto y este artículo te anima a ver. En el momento de escribir esto aún no he visto todo el contenido de este artículo pero con esta lista lo tengo anotado y pendiente de ver.

Películas

Series

Documentales

En YouTube hay cantidad de documentales más.

Documentales sobre el software libre y código abierto

Probablemente ya conozcas el software libre, o quizá no, en cualquier caso te recomiendo que veas una de las conferencias de Stallman da en numerosos lugares y en las que da a conocer el software libre y promueve su uso. Cuales son los motivos éticos, morales, de seguridad y prácticos por los que preferir software libre en vez de software privativo. Si te convence y usas otros sistemas operativos como Windows o MacOS puedes empezar a conocer las distribuciones de GNU/Linux y elegir una distribución GNU/Linux de entre las varias que hay disponibles.

GNULinux

Libros y novelas

  • El circulo de Dave Eggers
  • Microsiervos de Douglas Coupland
  • Cuando los administradores de sistemas gobernaron la tierra de Cory Doctorow
  • Criptonomicon de Neal Stephenson
  • Snow Crash de Neal Stephenson
  • Ready Player One de Ernest Cline
  • ¿Sueñan los androides con ovejas eléctricas? de Philip K. Dick
  • Algunas novelas sobre informática y hackers

¿Conoces alguno imprescindible más que incluir en esta lista?


Poesía Binaria: Creando una configuración personalizada para Emacs. Mi .emacs.d con explicaciones detalladas (II – Edición de código)

$
0
0


Suelo utilizar Emacs para editar código fuente para todos mis programas, tanto proyectos grandes como pequeños. Y, para crear muchos de los ejemplos que pongo en el blog (Aunque a veces la indentación no se copia bien). Por eso, es muy importante, tener herramientas para poder utilizar sin problemas y cómodamente el código en nuestro editor.

En este post, como ya empecé hace unos días (ver aquí Mi configuración personalizada para Emacs, primera parte) seguiré comentando punto por punto mi configuración actual de Emacs, con las utilidades a mi gusto. Aunque la configuración actualizada la tengo en GitHub, vamos a ver con detalle las partes dedicadas a ayuda de edición de código, orientado sobre todo a hacer más llevadera la programación con utilidades que mejoran nuestra experiencia de usuario y sesiones.

init.el

La configuración que muestro aquí está dividida en varios archivos está dividida en múltiples archivos divididos por categorías. En el post anterior vemos más detalles sobre el árbol de directorios y el contenido de init.el así como la forma en la que tenemos que incluir los archivos que presento aquí.

De todas formas, este código puede ser evaluado con M-x eval-expression / M-x eval-region (dependiendo de dónde lo escribamos). Incluso es recomendable abrir un buffer nuevo (o *scratch*) para practicar y probar cosas.

Gestión de sesiones – init-sessions.el

En este archivo:

  • Establecemos configuración de las sesiones y bloqueos. En mi caso, dependiendo del directorio desde el que se abra Emacs. Así como el auto-salvado de la sesión, a los 10 minutos (600 segundos)
  • Sobreescribo funciones de lectura del desktop para mostrar el tiempo invertido en restaurar el desktop.
  • Hacemos que se restauren más aspectos de la sesión de Emacs con el session-mode.
  • Se establecen valores por defecto del tamaño de los historiales que guardará Emacs. No los pongo ilimitados para que se optimice un poco el uso de la memoria y las búsquedas sean más rápidas.
  • Configuramos las copias de seguridad. Éstas utilizarán el modo backup-by-copying, aunque es el más lento, de otro modo, la copia de seguridad se hace renombrando y en algunos sistemas de archivos puede dar problemas. Por otro lado, se mantendrán varias versiones de los archivos y todas las copias de seguridad se harán en el directorio ./.__backups__ dentro del directorio actual. Muchos prefieren guardar las copias de seguridad en otro sitio, podemos darle una ruta absoluta si queremos guardar todas las copias de seguridad juntas.
  • También me gusta la forma en la que Emacs ofrece salvado automático de archivos aunque podremos establecer el directorio que deseemos.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
;; From https://github.com/purcell/emacs.d/blob/master/lisp/init-sessions.el
;; Using some settings from https://github.com/alexott/emacs-configs/blob/master/rc/emacs-rc-desktop.el
;; save a list of open files in ~/.emacs.d/.emacs.desktop

(setq-default desktop-missing-file-warning nil)

(setq emacs-sessions-directory (expand-file-name "sessions/" user-emacs-directory))
(setq emacs-desktop-file (concat "emacs_"(secure-hash 'md5 default-directory)".desktop"))
(setq emacs-desktop-lock (concat "lock_"(secure-hash 'md5 default-directory)))
(setq desktop-path (list emacs-sessions-directory)
      desktop-base-file-name      emacs-desktop-file
      desktop-base-lock-name      emacs-desktop-lock
      desktop-save                t
      desktop-files-not-to-save   "^$";reload tramp paths
      desktop-auto-save-timeout 600)
(desktop-save-mode 1)

(defadvice desktop-read (around time-restore activate)
    (let((start-time (current-time)))
      (prog1
          ad-do-it
        (message "Desktop restored in %.2fms"
                 (float-time (time-subtract (current-time)
                emacs-start-time))))))

(defadvice desktop-create-buffer (around time-create activate)
  (let((start-time (current-time))
        (filename (ad-get-arg 1)))
    (prog1
        ad-do-it
      (message "Desktop: %.2fms to restore %s"
               (float-time (time-subtract (current-time)
                  emacs-start-time))
               (when filename
         (abbreviate-file-name filename))))))

;;----------------------------------------------------------------------------
;; Restore histories and registers after saving
;;----------------------------------------------------------------------------
(setq-default history-length 1000)
(savehist-mode t)

(require-package 'session)

(setq session-save-file (expand-file-name ".session" user-emacs-directory))
(setq session-name-disable-regexp "\\(?:\\`'/tmp\\|\\.git/[A-Z_]+\'\\)")
(add-hook 'after-init-hook 'session-initialize)

;;----------------------------------------------------------------------------
;; Histories configuration
;;----------------------------------------------------------------------------
;; save a bunch of variables to the desktop file
;; for lists specify the len of the maximal saved data also
(setq desktop-globals-to-save
      (append '((comint-input-ring        .50)
                (compile-history          .30)
                desktop-missing-file-warning
                (dired-regexp-history     .20)
                (extended-command-history .30)
                (face-name-history        .20)
                (file-name-history        .100)
                (grep-find-history        .30)
                (grep-history             .30)
                (ido-buffer-history       .100)
                (ido-last-directory-list  .100)
                (ido-work-directory-list  .100)
                (ido-work-file-list       .100)
                (ivy-history              .100)
                (magit-read-rev-history   .50)
                (minibuffer-history       .50)
                (org-clock-history        .50)
                (org-refile-history       .50)
                (org-tags-history         .50)
                (query-replace-history    .60)
                (read-expression-history  .60)
                (regexp-history           .60)
                (regexp-search-ring       .20)
                register-alist
                (search-ring              .20)
                (shell-command-history    .50)
                tags-file-name
                tags-table-list)))

(when(eval-when-compile (and(>= emacs-major-version 24)
                              (version< emacs-version "24.3.50")
                              ))
  (unless (boundp 'desktop-restore-frames)
    (require-package 'frame-restore)
    (frame-restore)))

;;----------------------------------------------------------------------------
;; Backups and auto-save
;;----------------------------------------------------------------------------
;; Backups management (file~)
(setq backup-by-copying t)
(setq backup-directory-alist '((".".".__backups__")))
(setq delete-old-versions t
      kept-new-versions 6
      kept-old-versions 2
      version-control t)

;; Autosave backups management (#file#) Emacs Wiki, https://www.emacswiki.org/emacs/AutoSave
(defconst emacs-tmp-dir (expand-file-name (format "emacs%d"(user-uid)) temporary-file-directory))
(setq auto-save-file-name-transforms
        `((".*",emacs-tmp-dir t)))
    (setq auto-save-list-file-prefix
          emacs-tmp-dir)


(myemacs/elapsed-time)
(provide 'init-sessions)

init-cedet.el

EMACS CEDET Screenshot
CEDET (Collection of Emacs Development Environment Tools o Colección de Herramientas de Entorno de Desarrollo de Emacs) es una de las joyas de Emacs que permiten aumentar la funcionalidad de este editor a otro nivel. En CEDET tenemos muchas utilidades que harán que programemos mucho más rápido. Algunos de los módulos que encontramos en CEDET son:

  • Semantic: Analiza nuestro código para ofrecernos autocompletado inteligente, resaltar declaraciones de funciones, buscar implementaciones, navegar dentro de nuestro código, buscar errores, etc.
  • EDE: Emacs Development Environment (Entorno de Desarrollo de Emacs), nos proporciona un entorno de gestión de proyectos.
  • SRE: Gestor de plantillas
  • ECB: Emacs Code Browser (Navegador de Código de Emacs). Nos presentará una configuración de ventanas en la que incluirá un navegador de archivos y directorios, un navegador de objetos dentro de nuestro código y un historial además de la ventana de nuestro código. Es más o menos lo que se ve en la imagen de este apartado.
  • Otros: Podemos instalar una barra de acceso rápido a elementos (Speedbar), tenemos un módulo para crear diagramas (COGRE), y muchas funciones Lisp que pueden utilizar otros módulos para Emacs.

Nuestro archivo init-cedet.el será el siguiente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
;; Based on Alex Ott's minimal-cedet-config : https://gist.github.com/alexott/3930120
;; and others

;; Use builtin CEDET instead of installing new CEDET version
(require 'cedet)
;; Load contrib directory
(add-to-list 'load-path (file-name-as-directory (expand-file-name "site-lisp/cedet-contrib/" user-emacs-directory))"contrib")

;; Workarounds for Emacs < 25
(when(eval-when-compile (version<"25" emacs-version))
  (setq fixes-root-path (file-name-as-directory (expand-file-name "lisp/fixes/" user-emacs-directory)))
  (load-file (concat fixes-root-path "hideif.el")))

;; SemanticDB installed by default
(add-to-list 'semantic-default-submodes 'global-semanticdb-minor-mode t)
;; Buffer parsing when idle
(add-to-list 'semantic-default-submodes 'global-semantic-idle-scheduler-mode 1)
;; Display current function on header
(add-to-list 'semantic-default-submodes 'global-semantic-stickyfunc-mode 1)
;; Highlight the declaration of current function
(add-to-list 'semantic-default-submodes 'global-semantic-highlight-func-mode 1)
;; Display function info in minibuffer
(add-to-list 'semantic-default-submodes 'global-semantic-idle-summary-mode t)
;; srecode!!
(add-to-list 'semantic-default-submodes 'global-srecode-minor-mode t)
;; Show parser status when parsing lasts long
(add-to-list 'semantic-default-submodes 'global-show-parser-state-mode t)
;; Decorates some parts of the file due to the position where we are
(add-to-list 'semantic-default-submodes 'global-semantic-decoration-mode t)
;; Underline in red everything it cannot parse
(add-to-list 'semantic-default-submodes 'global-semantic-show-unmatched-syntax-mode)
;; Most Recently Used tags/functions fast jump
(add-to-list 'semantic-default-submodes 'global-semantic-mru-bookmark-mode)

(setq-default semantic-idle-scheduler-idle-time 5);I'm idle when I've been 5 seconds out
(setq-default semantic-idle-scheduler-verbose-flag 't);Tell me when you are working

(semantic-mode)

;; These may be automatically required, but I had them from previous versions
(require 'semantic/bovine/c)
(require 'semantic/bovine/gcc)
(require 'semantic/bovine/grammar)
(require 'semantic/bovine/make)
(require 'semantic/bovine/scm)
(require 'semantic/idle)
(require 'semantic/db)
(require 'semantic/ia)
(require 'semantic/analyze)
(require 'eassist)

;; customisation of modes
(defun gasparfm/cedet-hook ()
  (local-set-key [(control return)] 'semantic-ia-complete-symbol-menu)
  (local-set-key "\C-c?" 'semantic-ia-complete-symbol)
  ;;
  (local-set-key "\C-c>" 'semantic-complete-analyze-inline)
  (local-set-key "\C-c=" 'semantic-decoration-include-visit)
  ;; C-c+j = Fast jump
  (local-set-key "\C-cj" 'semantic-ia-fast-jump)
  ;; C-c+q = Show doc
  (local-set-key "\C-cq" 'semantic-ia-show-doc)
  ;; C-c+s Show Summary
  (local-set-key "\C-cs" 'semantic-ia-show-summary)
  ;; C-c+p = Toggle between prototype and function
  (local-set-key "\C-cp" 'semantic-analyze-proto-impl-toggle)
  ;; C-c+m = Switch MRU tags
  (local-set-key "\C-cp" 'semantic-mrub-switch-tags)
  )

(add-hook 'c-mode-common-hook 'gasparfm/cedet-hook)
(add-hook 'lisp-mode-hook 'gasparfm/cedet-hook)
(add-hook 'scheme-mode-hook 'gasparfm/cedet-hook)
(add-hook 'emacs-lisp-mode-hook 'gasparfm/cedet-hook)
(add-hook 'erlang-mode-hook 'gasparfm/cedet-hook)

(add-to-list 'semantic-dependency-system-include-path '/usr/include/c++/5/bits)
(semanticdb-enable-gnu-global-databases 'c-mode t)
(semanticdb-enable-gnu-global-databases 'c++-mode t)

;; SRecode
;; Delete ~/.emacs.d/srecode-map.el if any problems loading
(require 'srecode)
(global-srecode-minor-mode 1)

;; EDE
(require 'ede)
(global-ede-mode 1)
(ede-enable-generic-projects)

(require-package 'ecb)
(setq-default ecb-tip-of-the-day nil)

(myemacs/elapsed-time)
(provide 'init-cedet)

Lo primero que hacemos es cargar el módulo cedet que viene con Emacs. Aunque podemos descargarlo de Internet, con muchos módulos y más completo, el que viene con Emacs suele ser más nuevo y compatible con nuestra versión de Emacs. Puede que si lo descargamos, aunque sea desde la página oficial, tengamos ciertos problemas para echarlo a andar.

Luego cargamos en el load-path de Emacs el contenido de site-lisp/cedet-contrib. Dentro de este directorio (que podemos ver en el GitHub de mi configuración) he incluido algunos archivos que vienen con la instalación original de CEDET y no vienen en el core, pero que son interesantes.

Además, incluyo un archivo hideif.el que suele venir con Emacs y, tiene un bug en versiones inferiores a Emacs 25 (creo que en la 24.5 ya lo arreglaron, pero no llegué a probarlo). CEDET utiliza este módulo internamente y puede provocar algunos avisos, incluso hacer que no inicie Emacs correctamente.

Una vez cargado el módulo, vemos qué partes vamos a configurar. En mi caso, me quedo con Semantic, EDE, SREcode, ECB y EAssist (parte de los módulos de contrib que usan CEDET por debajo).

Semantic, que parsea el contenido de mis archivos de código leyendo variables, funciones, clases, espacios de nombres y demás lo suelo usar para asistirme mientras codifico. Esta asistencia suele ser el autocompletado inteligente, que me da opciones relativas a lo que estoy escribiendo. Puede darme opciones tanto del propio lenguaje como de bibliotecas que esté incluyendo y sean accesibles. Está pensado para C y C++, aunque para otros lenguajes también funciona bien y me permite trabajar rápidamente. Aunque está hecho en un lenguaje interpretado funciona muy rápido, e incluso hay formas de hacer que funcione más rápido aún, ayudándonos de aplicaciones externas, yo prefiero que mi configuración de Emacs sea lo más portable posible, y llevándome el directorio .emacs.d o incluso clonándolo de GitHub sea suficiente. También se usa para que ECB muestre información sobre los elementos que se encuentran en el código y pueda ir directamente a funciones, variables o estructuras determinadas o, por ejemplo, para que cuando programo en C o C++ pueda saltar directamente de un archivo de cabecera a uno de código sin tener que hacerlo a mano, dirigiéndome directamente al número de línea concreto donde está una función, una llamada, etc.

Aunque Semantic tiene muchas posibles configuraciones, en mi configuración personal incluiré:

  • semanticdb-minor-mode. Como parsear archivos de código fuente es una tarea pesada que puede dejar a Emacs colgado durante varios segundos o minutos. Haremos que Semantic sólo lea los archivos cuando haya cosas nuevas, almacenando en caché los elementos que ya ha leído y optimizando así su funcionamiento.
  • global-semantic-idle-scheduler-mode. Analiza archivos cuando no estoy directamente trabajando con Emacs. Porque es muy incómodo que, mientras estás trabajando, el editor se pare y no te deje hacer nada, aunque sea durante unos segundos. Por eso, aunque a veces, cuando utilizamos el autocompletado puede forzarse un parseo de los archivos, es interesante que, mientras no estemos trabajando se vayan completando ciertas partes del mismo. Personalmente, lo suelo configurar con semantic-idle-scheduler-idle-time indicando que se considere que no hago nada cuando lleve 5 segundos sin utilizar Emacs, y semantic-idle-scheduler-verbose-flag para que me indique en pantalla cuándo está trabajando Semantic.
  • global-semantic-stickyfunc-mode coloca en la parte superior del buffer el prototipo de la función dentro de la que estamos. Es muy útil cuando las funciones son muy grandes, o nuestra pantalla muy pequeña, para tener siempre presente el nombre de la función y los argumentos de entrada.
  • global-semantic-highlight-func-mode. Resalta llamadas o declaraciones de funciones mientras tenemos el cursor encima. Así sabemos dónde se llama, y dónde se declara una función.
  • global-semantic-idle-summary-mode. Nos muestra la cabecera de las funciones en el minibuffer ayudándonos a ver los argumentos de una función, método o llamada de un vistazo.
  • srecode-minor-mode. Activa el sistema de plantillas SREcode.
  • global-show-parser-state-mode. Cuando esté realizando un parseo de archivos fuente, decirnos el progreso del parseo en el minibuffer. Siempre es algo más lento decir cuánto lleva completado de una tarea e ir actualizando ese progreso, pero desespera menos al que pacientemente está mirando pasmado la pantalla esperando que se realice la tarea.
  • global-semantic-decoration-mode. Aplica una decoración a cada palabra clave del código.
  • global-semantic-show-unmatched-syntax-mode. Nos avisa cuando hay errores de sintaxis según el parseador. Puede que algo no sea un error de sintaxis propiamente dicho, en códigos muy complejos, tal vez el parser no llegue a lo que nosotros queremos, pero generalmente funciona bien.
  • global-semantic-mru-bookmark-mode. Nos permite saltar entre etiquetas (variables, funciones, etc) para dirigirnos a la parte del código donde se declaran/definen/llaman con M-n, M-p
  • También establecemos algunas teclas rápidas para llamar algunos métodos de semantic. Podemos verlo al final del post, en teclas rápidas y se añaden a un hook de determinados lenguajes.

Tras esto, se añaden algunas características para mejorar el parseo de las bibliotecas de C y C++.

Utilidades de edición (init-editing-utils.el)

Ahora vamos a ver algunas utilidades que nos ayudarán a utilizar nuestro entorno de programación, o configurarlo de manera fina, a nuestro gusto. Nota: El gusto de cada uno puede variar, aunque aquí tenéis un punto de partida.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
;; From : https://github.com/purcell/emacs.d/blob/master/lisp/init-editing-utils.el
;; and others

(require-package 'diminish)
(require-package 'unfill)
(require-package 'focus)

;; Automatic pairs open symbols (, {, [...
;; (when (fboundp 'electric-pair-mode)
;;   (electric-pair-mode))
(when(eval-when-compile (version<"24.4" emacs-version))
  (electric-indent-mode 1))

;;----------------------------------------------------------------------------
;; Some basic preferences
;;----------------------------------------------------------------------------
(setq-default
 blink-cursor-interval 0.4
 bookmark-default-file (expand-file-name ".bookmarks.el" user-emacs-directory)
 buffers-menu-max-size 30
 case-fold-search t
 column-number-mode t
 delete-selection-mode t
 ediff-split-window-function 'split-window-horizontally
 ediff-window-setup-function 'ediff-setup-windows-plain
 indent-tabs-mode t
 make-backup-files t
 auto-save-interval 180                                ;Auto save every 180 secs
 mouse-yank-at-point t
 save-interprogram-paste-before-kill t
 scroll-preserve-screen-position 'always
 set-mark-command-repeat-pop t
 tooltip-delay 0.5
 truncate-lines nil
 truncate-partial-width-windows nil)

(global-auto-revert-mode)
(setq global-auto-revert-non-file-buffers t
      auto-revert-verbose nil)

(transient-mark-mode t)

;;; Newline behaviour
(defun sanityinc/newline-at-end-of-line ()
  "Move to end of line, enter a newline, and reindent."
  (interactive)
  (move-end-of-line 1)
  (newline-and-indent))

(when(eval-when-compile (string<"24.3.1" emacs-version))
  ;; https://github.com/purcell/emacs.d/issues/138
  (after-load 'subword
    (diminish 'subword-mode)))

;; Some bugs with indent-guide-mode and autocomplete-mode
;; (when (maybe-require-package 'indent-guide)
;;   (add-hook 'prog-mode-hook 'indent-guide-mode)
;;   (after-load 'indent-guide
;;     (diminish 'indent-guide-mode)))

;;(require-package 'nlinum)

(when(require-package 'rainbow-delimiters)
  (add-hook 'prog-mode-hook 'rainbow-delimiters-mode))

(when(fboundp 'global-prettify-symbols-mode)
  (global-prettify-symbols-mode))

(require-package 'undo-tree)
(global-undo-tree-mode)
(diminish 'undo-tree-mode)


(require-package 'highlight-symbol)
(dolist(hook '(prog-mode-hook html-mode-hook css-mode-hook))
  (add-hook hook 'highlight-symbol-mode)
  (add-hook hook 'highlight-symbol-nav-mode))
(add-hook 'org-mode-hook 'highlight-symbol-nav-mode)
(after-load 'highlight-symbol
  (diminish 'highlight-symbol-mode)
  (defadvice highlight-symbol-temp-highlight (around sanityinc/maybe-suppress activate)
    "Suppress symbol highlighting while isearching."
    (unless (or isearch-mode
                (and(boundp 'multiple-cursors-mode) multiple-cursors-mode))
      ad-do-it)))

;;----------------------------------------------------------------------------
;; Zap *up* to char is a handy pair for zap-to-char
;;----------------------------------------------------------------------------
(autoload 'zap-up-to-char "misc""Kill up to, but not including ARGth occurrence of CHAR.")
(global-set-key (kbd "M-Z") 'zap-up-to-char)

;;----------------------------------------------------------------------------
;; Browse kill ring
;;----------------------------------------------------------------------------
(require-package 'browse-kill-ring)
(setq browse-kill-ring-highlight-inserted-item 'pulse)
(setq browse-kill-ring-highlight-current-entry t)
(setq browse-kill-ring-show-preview t)
(setq browse-kill-ring-separator "\f")
(global-set-key (kbd "s-Y") 'browse-kill-ring)
(after-load 'browse-kill-ring
  (define-key browse-kill-ring-mode-map (kbd "C-g") 'browse-kill-ring-quit)
  (define-key browse-kill-ring-mode-map (kbd "M-n") 'browse-kill-ring-forward)
  (define-key browse-kill-ring-mode-map (kbd "M-p") 'browse-kill-ring-previous)
  (define-key browse-kill-ring-mode-map (kbd "<down>") 'browse-kill-ring-forward)
  (define-key browse-kill-ring-mode-map (kbd "<up>") 'browse-kill-ring-previous)
  (define-key browse-kill-ring-mode-map (kbd "<backtab>") 'browse-kill-ring-previous);Shift + TAB
  )
(after-load 'page-break-lines
  (push 'browse-kill-ring-mode page-break-lines-modes))

;; Forward yank
(defun yank-pop-forwards (arg)
      (interactive "p")
      (yank-pop (- arg)))
    (global-set-key "\M-Y" 'yank-pop-forwards); M-Y (Meta-Shift-Y)

;;----------------------------------------------------------------------------
;; Don't disable narrowing commands
;;----------------------------------------------------------------------------
(put 'narrow-to-region 'disabled nil)
(put 'narrow-to-page 'disabled nil)
(put 'narrow-to-defun 'disabled nil)

;;----------------------------------------------------------------------------
;; Show matching parens
;;----------------------------------------------------------------------------
(show-paren-mode 1)

;;----------------------------------------------------------------------------
;; Expand region
;;----------------------------------------------------------------------------
(require-package 'expand-region)
(global-set-key (kbd "C-=") 'er/expand-region)

;;----------------------------------------------------------------------------
;; Don't disable case-change functions
;;----------------------------------------------------------------------------
(put 'upcase-region 'disabled nil)
(put 'downcase-region 'disabled nil)

;;----------------------------------------------------------------------------
;; Don't disable these functions
;;----------------------------------------------------------------------------
(put 'scroll-left 'disabled nil)
(put 'eval-expression 'disabled nil)

;;----------------------------------------------------------------------------
;; Rectangle selections, and overwrite text when the selection is active
;;----------------------------------------------------------------------------
(cua-selection-mode t)                 ; for rectangles, CUA is nice


;; Dont delete selected text when pressing key. C-w
(setq delete-selection-mode nil)

;;----------------------------------------------------------------------------
;; Handy key bindings
;;----------------------------------------------------------------------------
(global-set-key (kbd "C-.") 'set-mark-command)
(global-set-key (kbd "C-x C-.") 'pop-global-mark)

;; (when (maybe-require-package 'avy)
;;   (global-set-key (kbd "C-;") 'avy-goto-word-or-subword-1))

(require-package 'multiple-cursors)
;; multiple-cursors
(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
(global-set-key (kbd "C->") 'mc/mark-next-like-this)
(global-set-key (kbd "C-+") 'mc/mark-next-like-this)
(global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)
;; From active region to multiple cursors:
(global-set-key (kbd "C-c c r") 'set-rectangular-region-anchor)
(global-set-key (kbd "C-c c c") 'mc/edit-lines)
(global-set-key (kbd "C-c c e") 'mc/edit-ends-of-lines)
(global-set-key (kbd "C-c c a") 'mc/edit-beginnings-of-lines)


;; Train myself to use M-f and M-b instead
(global-unset-key [M-left])
(global-unset-key [M-right])



(defun kill-back-to-indentation ()
  "Kill from point back to the first non-whitespace character on the line."
  (interactive)
  (let((prev-pos (point)))
    (back-to-indentation)
    (kill-region (point) prev-pos)))

(global-set-key (kbd "S-<backspace>") 'kill-back-to-indentation)

;;----------------------------------------------------------------------------
;; Page break lines
;;----------------------------------------------------------------------------
(require-package 'page-break-lines)
(global-page-break-lines-mode)
(diminish 'page-break-lines-mode)

;;----------------------------------------------------------------------------
;; Shift lines up and down with M-up and M-down. When paredit is enabled,
;; it will use those keybindings. For this reason, you might prefer to
;; use M-S-up and M-S-down, which will work even in lisp modes.
;;----------------------------------------------------------------------------
(require-package 'move-dup)
;(global-set-key [M-up] 'md/move-lines-up)
;(global-set-key [M-down] 'md/move-lines-down)
;(global-set-key [M-S-up] 'md/move-lines-up)
;(global-set-key [M-S-down] 'md/move-lines-down)

(global-set-key (kbd "C-c d") 'md/duplicate-down)
(global-set-key (kbd "C-c D") 'md/duplicate-up)

;;----------------------------------------------------------------------------
;; Fix backward-up-list to understand quotes, see http://bit.ly/h7mdIL
;;----------------------------------------------------------------------------
(defun backward-up-sexp (arg)
  "Jump up to the start of the ARG'th enclosing sexp."
  (interactive "p")
  (let((ppss (syntax-ppss)))
    (cond((elt ppss 3)
           (goto-char (elt ppss 8))
           (backward-up-sexp (1- arg)))
          ((backward-up-list arg)))))

(global-set-key [remap backward-up-list] 'backward-up-sexp); C-M-u, C-M-up


;;----------------------------------------------------------------------------
;; Cut/copy the current line if no region is active
;;----------------------------------------------------------------------------
(require-package 'whole-line-or-region)
(whole-line-or-region-mode t)
(diminish 'whole-line-or-region-mode)
(make-variable-buffer-local 'whole-line-or-region-mode)

(defun suspend-mode-during-cua-rect-selection (mode-name)
  "Add an advice to suspend `MODE-NAME' while selecting a CUA rectangle."
  (let((flagvar (intern(format "%s-was-active-before-cua-rectangle" mode-name)))
        (advice-name (intern(format "suspend-%s" mode-name))))
    (eval-after-load 'cua-rect
      `(progn
         (defvar ,flagvar nil)
         (make-variable-buffer-local ',flagvar)
         (defadvice cua--activate-rectangle (after ,advice-name activate)
           (setq,flagvar (and(boundp ',mode-name),mode-name))
           (when,flagvar
             (,mode-name 0)))
         (defadvice cua--deactivate-rectangle (after ,advice-name activate)
           (when,flagvar
             (,mode-name 1)))))))

(suspend-mode-during-cua-rect-selection 'whole-line-or-region-mode)




(defun sanityinc/open-line-with-reindent (n)
  "A version of `open-line' which reindents the start and end positions.
If there is a fill prefix and/or a `left-margin', insert them
on the new line if the line would have been blank.
With arg N, insert N newlines."

  (interactive "*p")
  (let* ((do-fill-prefix (and fill-prefix (bolp)))
     (do-left-margin (and(bolp)(>(current-left-margin)0)))
     (loc (point-marker))
     ;; Don't expand an abbrev before point.
     (abbrev-mode nil))
    (delete-horizontal-space t)
    (newline n)
    (indent-according-to-mode)
    (when(eolp)
      (delete-horizontal-space t))
    (goto-char loc)
    (while (> n 0)
      (cond((bolp)
         (if do-left-margin (indent-to (current-left-margin)))
         (if do-fill-prefix (insert-and-inherit fill-prefix))))
      (forward-line 1)
      (setq n (1- n)))
    (goto-char loc)
    (end-of-line)
    (indent-according-to-mode)))

;;----------------------------------------------------------------------------
;; Random line sorting
;;----------------------------------------------------------------------------
(defun sort-lines-random (beg end)
  "Sort lines in region randomly."
  (interactive "r")
  (save-excursion
    (save-restriction
      (narrow-to-region beg end)
      (goto-char (point-min))
      (let;; To make `end-of-line' and etc. to ignore fields.
          ((inhibit-field-text-motion t))
        (sort-subr nil 'forward-line 'end-of-line nilnil
                   (lambda(s1 s2)(eq(random2)0)))))))




(require-package 'highlight-escape-sequences)
(hes-mode)

(require-package 'guide-key)
(setq guide-key/guide-key-sequence '("C-x""C-c""C-x 4""C-x 5""C-c ;""C-c ; f""C-c ' f""C-x n""C-x C-r""C-x r""M-s""C-h"))
(add-hook 'after-init-hook
          (lambda()
            (guide-key-mode 1)
            (diminish 'guide-key-mode)))

;;----------------------------------------------------------------------------
;; More useful things
;;----------------------------------------------------------------------------
(set-default 'truncate-lines t)
(setq show-trailing-whitespace nil)
(setq site-lisp-path (file-name-as-directory (expand-file-name "site-lisp/" user-emacs-directory)))

(require-package 'fill-column-indicator)

;; ;; Global-FCI-mode can cause problems, like minibuffer with fci or information windows
;; ;; drawing incorrectly.
(define-globalized-minor-mode global-fci-mode fci-mode (lambda()(fci-mode 1)))
(defvar fci-mode nil)
;; (global-fci-mode 1)
(add-hook 'prog-mode-hook 'my/fci-mode-stuff)

(defun my/fci-mode-stuff ()
    (fci-mode 1)
    (setq fci-rule-color "dim gray")
    (setq fci-rule-width 2)
    (setq fci-rule-column 130)
    ;; Workaround because of implementation of show-trailing-whitespace https://github.com/alpaker/Fill-Column-Indicator/blob/master/fill-column-indicator.el
    (setq whitespace-style '(face trailing))
    ;; fci-mode not compatible with non-nil values of hl-line-sticky-flag
    (setq hl-line-sticky-flag nil))

;; (set-fill-column 130)
;; (global-whitespace-mode)
;;
;; Word Count Mode with Goals. Can be activated, Run in Modeline
(require-package 'wc-mode)
; hideshowvis for hs-mode
;(require-package 'hideshowvis)
(dolist(hook (list 'emacs-lisp-mode-hook
                    'java-mode-hook
                    'lisp-mode-hook
                    'perl-mode-hook
                    'hs-mode-hook
                    'c-mode-common-hook))
  (add-hook hook 'hs-minor-mode))
;; From EmacsWiki expand hidden block when searching for line
(defadvice goto-line (after expand-after-goto-line
                            activate compile)
  "hideshow-expand affected block when using goto-line in a collapsed buffer"
  (save-excursion
    (hs-show-block)))

;; Echo keystrokes
(setq echo-keystrokes 0.1)

;; Copy/Paste behaviour. Use X clipboard.
(setq x-select-enable-primary t)
(setq x-select-enable-clipboard nil)

;; Emacs Scroll like any other editor kaka pedo culo pis
(setq scroll-step            1
      scroll-conservatively  10000)
;; Show line numbers when using go-to line when line numbers are not enabled
;; Stolen from http://whattheemacsd.com/key-bindings.el-01.html
(global-set-key [remap goto-line] 'goto-line-with-feedback)

(defun goto-line-with-feedback ()
  "Show line numbers temporarily, while prompting for the line number input"
  (interactive)
  (unwind-protect
      (progn
        (linum-mode 1)
        (goto-line (read-number "Goto line: ")))
    (linum-mode -1)))

;; Delete trailing whitespace before saving fil
(add-hook 'before-save-hook 'delete-trailing-whitespace)

;;----------------------------------------------------------------------------
;; Key definitions
;;----------------------------------------------------------------------------
(global-set-key (kbd "RET") 'newline-and-indent)
(global-set-key (kbd "s-<return>") 'sanityinc/newline-at-end-of-line)

(global-set-key (kbd "C-<up>") 'backward-paragraph)
(global-set-key (kbd "C-<down>") 'forward-paragraph)
(global-set-key (kbd "C-<left>") 'backward-word)
(global-set-key (kbd "C-<right>") 'forward-word)

(global-set-key [(s-backspace)] 'undo)
(global-set-key [(s-S-backspace)] 'redo);Meta -Shift-Backspace
(global-set-key (kbd "C-o") 'sanityinc/open-line-with-reindent)

;; Mouse horizontal scrolling (in case your mouse has horizontal scroll)
(global-set-key [mouse-6] 'scroll-right)
(global-set-key [mouse-7] 'scroll-left)

;; Sometimes my laptop can't autoindent
(global-set-key (kbd "C-M-º") 'indent-region)

;; hs-mode key bindings I like
(global-set-key (kbd "M-+") 'hs-show-block)
(global-set-key (kbd "M--") 'hs-hide-block)

;; Join lines on M-j
(global-set-key (kbd "M-j")(lambda()(interactive)(join-line -1)))

;; Focus mode toggle
(global-set-key (kbd "s-º")(lambda()(interactive)(focus-mode 'toggle)))

;; Browse kill ring
(global-set-key (kbd "C-c k") 'browse-kill-ring)

;; Highlight symbol
(global-set-key (kbd "M-s-h") 'highlight-symbol)

;; CUA for rectangular selections
(global-set-key (kbd "M-RET") 'cua-rectangle-mark-mode)

;; Smarter move to the beginning of the line
;; remap C-a or to `smarter-move-beginning-of-line'
(global-set-key [remap move-beginning-of-line]
                'sacha/smarter-move-beginning-of-line)

(myemacs/elapsed-time)
(provide 'init-editing-utils)

Ahora unas cuantas explicaciones sobre lo que se ha hecho en este archivo:

  • Se activa el modo diminish, que nos ayuda a mantener limpia la modeline. Como vemos, en la modeline se muestran los modos que hay activados, y cada vez tenemos más minor-modes activados, que añaden pequeñas funcionalidades a Emacs. En este caso, hay muchos modos, que si no aparecen, no pasa nada. Así ocultamos algunos, como undo-tree que estará siempre activado.
  • Unfill, combina párrafos o regiones enteras (texto seleccionado) a una sola línea. Al contrario que fill mode que viene con Emacs. Podemos usarlo llamando M-x unfill-paragraph / unfill-region / unfill-toggle
  • Focus mode resalta el párrafo actual. Nos ayuda a centrarnos en una parte del código a medida que nos vamos moviendo. También disponemos entre otras utilidades de M-x focus-pin / focus-unpin que resaltan una región del código de forma permanente.
  • Activa electric-indent-mode para anidar código automáticamente al pulsar enter.
  • Activa auto-revert-mode que recarga automáticamente archivos modificados fuera de Emacs. Es muy útil para ver logs (como auto-revert-tail-mode) entre muchos otros casos.
  • Activa transient-mark-mode para poder seleccionar texto con C-SPC
  • Subword mode no está activado por defecto. Nos ayudará a saltar entre sub-palabras dentro de una frase. Por ejemplo, si creamos FuncionConUnNombreMuyLargo o funcion_con_un_nombre_muy_largo nos permitirá dirigirnos a cada una de las palabras con Control+Flechas.
  • Activa rainbow-delimiters que coloreará llaves, corchetes, paréntesis y demás de diferente color mientras programamos y los casará con sus correspondientes, para que no nos liemos mucho.
  • Prettify-symbols-mode permite cambiar el estilo o la representación de ciertos símbolos. Por ejemplo, cuando escribimos lambda() pone λ, sin cambiar el código, sólo cambia la visualización.
  • ¡Undo-tree es de mis preferidos! Si estás harto o harta de cómo Emacs gestiona el deshacer/rehacer, ¡este es tu modo! Nos muestra un árbol de modificaciones de nuestro código en el que podremos regresar a un estado anterior o, incluso si hemos deshecho cambios y hecho modificaciones, podremos volver a uno de los estados anteriores navegando entre ramas. Se activa con C-x u (como el deshacer normal), podemos navegar con las flechas y salir con la q.
  • highlight-symbol resalta el símbolo bajo el cursor. Aunque utilizo Semantic para navegar ṕor los símbolos con M-p y M-s, puedo utilizar M-s-h para resaltar el símbolo. (Configurado en las teclas rápidas).
  • Browse-kill-ring nos permite navegar por el portapapeles. Así para pegar un texto copiado anteriormente no tendremos que pulsar C-y y luego M-y hasta dar con el que queremos, podemos disponer de un menú interactivo y una previsualización muy chula gracias a browse-kill-ring-highlight-inserted-item, browse-kill-ring-highlight-current-entry, browse-kill-ring-show-preview y browse-kill-ring-separator
  • Previene la desactivación de los narrow-commands. Éstos permiten mostrar sólo parte del código, sin borrarlo. Es accesible desde C-x n n (para mostrar sólo la región seleccionada), C-x n d (para mostrar la función seleccionada) y C-x n w (para mostrar todo de nuevo).
  • Activa show-paren-mode, que muestra los paréntesis, llaves o corchetes que casan con el carácter actual. Junto con rainbow-delimiters son una joya para la edición de código.
  • Activa expand-region, que nos permitirá expandir y contraer la región seleccionada con C-= y C-0.
  • No desactiva upcase-region y downcase region para poner el texto seleccionado en mayúsculas (C-x C-u) o minúsculas (C-x C-l)
  • No desactiva el scroll lateral (C-x ). Eso sí, es incompatible con fci-mode.
  • No desactiva eval-expression (M-:)
  • Activa el cua-mode para hacer selecciones rectangulares. Yo lo activo con M-RET.
  • Evita que eliminemos texto cuando tenemos una selección abierta y pulsamos una tecla. Por ejemplo Cuando hacemos Control+Espacio y seleccionamos y luego escribimos una letra. Definiendo delete-selection-mode a nil no se borrarán las regiones.
  • Activamos multiple-cursors para trabajar en varios lugares a la vez. Podemos navegar con varias teclas que explico al final del post. Es complicado, pero cuando le coges el truco está muy bien.
  • Activamos page-break-lines y lo ocultamos. Este modo dibuja una línea horizontal cuando encuentra saltos de página (representados por ^L). Por cierto, para insertar un salto de página debemos pulsar C-q C-l. Tenemos formas de navegar por páginas, pero no lo uso demasiado. Sobre todo me gusta para la representación de los separadores del kill-ring con la extensión mencionada anteriormente.
  • Modo whole-line-or-region, hace que operaciones como copiar y cortar que afectan a regiones, o selecciones de texto, si no hay texto seleccionado afecten a la línea actual. Y como este modo no es compatible con CUA, lo desactivamos cuando utilizamos la selección rectangular.
  • Modo highlight-escape-sequences para resaltar las secuencias de escape como \n, \r, \t…
  • Modo guide-key que nos presentará ayuda sobre las teclas y sus funciones cuando empecemos a teclear una combinación de teclas como C-x C-c y demás. Podemos configurar el disparo de la ayuda con la combinación de teclas que deseemos.
  • Modo fill-column-indicator muestra una línea vertical en la columna 130, aunque podemos cambiar la columna. Lo malo de este modo es que no es compatible con muchas extensiones de Emacs. Actualmente le estoy dando una oportunidad, pero muchas veces suelo desactivarlo a mano.
  • Modo wc (Word count) que cuenta palabras dentro del buffer. Lo podemos activar a mano con M-x. Está bien, pero me llena la modeline de letras y la mayoría de las veces no me interesa. Adedmás, siempre podemos hacer M-x count-words.
  • Modo hs. Sirve para plegar el código, ocultando fragmentos que no nos interesan. Con M-+ y M– podemos plegar el código entre llaves, incluso funciones o clases enteras para visualizar en la pantalla sólo lo que necesitamos. Además, si utilizamos goto-line para ir a una línea determinada o hacemos una búsqueda dentro de un código oculto, éste se expandirá automáticamente.
  • Cuando utilizamos goto-line, mostramos los números de línea aunque estén desactivados.
  • Elimina los espacios (o los retornos de carro) sobrantes de los buffers antes de salvarlos. Eso nos ahorrará mucho tiempo, salvaremos archivos más pequeños y nos quitará muchos problemas.

Definimos algunas funciones nuevas con utilidades:

  • newline-at-end-of-line : Salta al fin de la línea, y crea una nueva línea. Una alternativa más corta que pulsar la tecla Fin y luego Enter. (La colocaremos en la tecla con s-RET).
  • yank-pop-forwards : Navega hacia atrás por el kill-ring. Es muy útil porque a veces queremos pegar un texto copiado hace mucho tiempo y pegamos, pegamos y pegamos y de repente nos pasamos y tenemos que dar la vuelta por todo el kill-ring para encontrar el texto. Con esta función podemos avanzar en dirección contraria.
  • sort-lines-random : Ordena aleatoriamente las líneas de una región o de un buffer. Podemos llamarla con M-x.
  • Redefinimos la tecla inicio a la función sacha/smarter-move-beginning-of-line. La primera vez que vamos al principio de la línea se hace al primer carácter escrito, por lo que si tenemos una línea indentada podemos acceder directamente a lo importante.

También definimos algunos valores de configuración como por ejemplo:

  • El tiempo de parpadeo del cursor (blink-cursor-interval)
  • Mostrar el número de columna (con column-number-mode)
  • Activamos la creación de archivos de backup (con make-backup-files)
  • Definimos el tiempo de autosalvado a 180 segundos (con auto-save-interval)
  • Hacemos que el pegado sea en la posición del cursor (con mouse-yank-at-point)
  • Activamos el truncado de líneas (con truncate-lines a ‘t)
  • No mostramos los espacios al final de línea (con show-trailing-whitespace a nil).
  • Y muchas más.

Autocompletado


Algo básico en cualquier editor de código es que éste nos ayude a escribir. El desarrollo de un programa es algo muy duro para nosotros (que sea divertido es otra cosa) y actualmente tenemos ordenadores muy potentes por lo que podemos aprovechar esa potencia para evitarnos buscar y tener que recordar miles de cosas o, aunque las recordemos, al menos que sea rápido escribirlas, porque a veces hay funciones, variables o tipos con nombres muy largos que tenemos que escribir una y otra vez.

Así que, es importante contar con un buen sistema de autocompletado. En Emacs, disponemos, entre otros de auto-complete-mode y company-mode, al menos son los más grandes. A mí me gusta auto-complete-mode y este será mi fichero init-auto-complete.el:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
;; Auto-complete basic configuration.
;; We'll find more specific configuration in language files
(require 'auto-complete)
(require 'auto-complete-config)
(global-auto-complete-mode)
(setq ac-comphist-file (expand-file-name
              "ac-comphist.dat" user-emacs-directory))

;; (add-to-list 'ac-dictionary-directories (expand-file-name
;;                   "~/.emacs.d_new/elpa/auto-complete-20170124.1845/dict"))

;;a word must be 3 chars long before completition begins
(setq-default ac-auto-start 2)
(set-default 'ac-sources '(ac-source-abbrev ac-source-words-in-same-mode-buffers ac-source-dictionary ac-source-words-in-buffer))
(setq-default ac-ignore-case 'smart)
;; Do What I Mean mode - Changes TAB behaviour depending on context
(setq-default ac-dwim t)
(setq ac-quick-help-delay 1)
;; Limit candidate limit
(setq ac-candidate-limit 400)
;; Allow searches
(setq ac-use-menu-map t)
(setq ac-candidate-menu-min 0)

(defun ac-emacs-lisp-mode-setup ()
  (setq ac-sources (append
                    '(ac-source-dictionary
                      ac-source-features
                      ac-source-functions
                      ac-source-variables
                      ac-source-symbols)
                                        ac-sources))

  (setq ac-omni-completion-sources
        '(("\\<featurep\s+'" ac+-source-elisp-features)
          ("\\<require\s+'" ac+-source-elisp-features)
          ("\\<load\s+""   ac-source-emacs-lisp-features))))

(add-hook 'emacs-lisp-mode-hook 'ac-emacs-lisp-mode-setup)

(defun ac-eshell-mode-setup ()
    (auto-complete-mode 1)
  (setq ac-sources (append '(ac-source-abbrev
                             ac-source-words-in-same-mode-buffers
                             ac-source-files-in-current-dir)
                           ac-sources)))

(add-hook 'eshell-mode-hook 'ac-eshell-mode-setup)


(myemacs/elapsed-time)
(provide 'init-auto-complete)

En este fichero defino sólo la parte más básica. Cada lenguaje irá introduciendo sus propias formas de autocompletado. Por ejemplo C y C++ explorarán archivos fuente con Semantic y lo mezclarán con información sobre las bibliotecas; Lisp tendrá muchas funciones y símbolos definidas, otros lenguajes utilizarán parte de sus manuales para completar palabras, etc. Pero en este fichero sólo incluyo la configuración por defecto de auto-complete, lo activo globalmente, estblezco el fichero de historial y defino algunas preferencias:

  • El auto completado se dispara automáticamente al escribir 2 caracteres. (ac-auto-start)
  • Se buscarán posibilidades de autocompletado en abbrev (ac-source-abbrev), características de lisp (ac-source-features), palabras de buffers del mismo modo que el actual (ac-source-words-in-same-mode-buffers), palabras de diccionario (ac-source-dictionary) y palabras del propio buffer (ac-source-words-in-buffer)
  • Se establece una política inteligente para las mayúsculas y minúsculas en las búsquedas (ac-ignore-case ‘smart) con lo que no tendremos que preocuparnos por ello.
  • Do What I Mean Mode. Altera el comportamiento de la tecla TAB dependiendo del contexto en el que estemos, lo que la hace más intuitiva. (ac-dwim t)
  • Retrasa la aparición del popup de ayuda a 1 segunto (ac-quick-help-delay 1). A veces con menos retraso puede causar problemas porque tarde en salir menos que la ventana de las opciones de autocompletado.
  • Limitamos el número de candidatos a mostrar (ac-candidate-limit)
  • Nos permite filtrar los resultados del menú de autocompletado con C-s (ac-use-menu-map)
  • El mínimo de candidatos para mostrar el menú es 1 (ac-candidate-menu-min)
  • Adicionalmente se establecen fuentes de autocompletado para Lisp y Eshell

En el próximo post veremos características personalizadas para lenguajes de programación. En los lenguajes incluiré C, C++, PHP, Python, Javascript, Ruby, HTML, SQL, CSS, LUA y alguno más.

Teclas rápidas


Estas son algunas de las teclas rápidas que se han establecido en este post. En el último post de la serie haré un resumen de todas las teclas rápidas, para tenerlas a modo de chuleta rápida:

  • Control-RET (Control + Enter) [Semantic]: Menú de autocompletado de Semantic (en lugar del menú del modo auto-complete)
  • C-c ? [Semantic] : Muestra en un buffer aparte todas las posibilidades de autocompletar de una etiqueta. Muestra también cierta ayuda en línea de cada etiqueta.
  • C-c > [Semantic] : Muestra en un buffer aparte las posibilidades de autocompletar sin ayuda y todo revuelto.
  • C-c = [Semantic] : Visita el archivo incluido.
  • C-c j [Semantic] : Salta a la línea donde se declara una función. Hace un efecto visual con el salto indicándonos la cabecera, inicio y final de la función.
  • C-c q [Semantic] : Visualiza la documentación de las funciones. Por ejemplo con comentarios de Doxygen
  • C-c s [Semantic] : Muestra un resumen en el minibuffer de llamadas a funciones o métodos.
  • C-c p [Semantic] : Salta al prototipo de la función.
  • C-c m [Semantic] : Salta a una etiqueta determinada
  • M-Z (Alt + Shift + Z) : Borra hasta el carácter indicado.
  • s-y (Meta + y) / C-c k: Activa el navegador del portapapeles para pegar texto de forma más interactiva y precisa.
  • M-Y (Meta + Shift + Y): Si estamos navegando por el kill-ring a la antigua usanza (C-y M-y M-y…) con M-Y navegamos por el ring hacia atrás.
  • C-= (Control =): Expande el texto seleccionado. Nos permite seleccionar una región más grande, delimitada por paréntesis, llaves, corchetes, sentencias, etc.
  • C-0 (Control 0): Encoge el texto seleccionado con los mismos criterios de antes.
  • S-backspace (Shift + retroceder) : Borra la línea actual hasta la indentación.
  • C-c d: Clona la línea actual en la línea siguiente.
  • C-c D: Clona la línea actual en la línea anterior.
  • s-RET (Meta + Enter): Hace que estemos donde estemos en la línea (podemos estar al principio o en mitad), saltemos de línea. Sería como ir al final de la línea y pulsar Enter.
  • C- (Control + arriba): Retrocede un párrafo.
  • C- (Control + abajo): Avanza un párrafo.
  • C- (Control + izquierda): Retrocede una palabra.
  • C- (Control + derecha) Retrocede una palabra.
  • s-backspace (Meta + retroceder): Deshacer (lo que en el Emacs de siempre era C-x u).
  • s-S-backspace (Meta + Shift + retroceder): Rehacer
  • Los ratones que tienen scroll horizontal pueden usarlo.
  • C-M-º (Control + Alt + º): Indentar automáticamente la región. Porque muchas veces C-M-\ no funciona correctamente y porque en los teclados en español podemos pulsar esta combinación con una sola mano.
  • M-+ (Alt + +) : Mostrar un bloque previamente ocultado. Estos bloques pueden ser contenidos de funciones, bucles, comentarios, etc.
  • M– (Alt + -) : Ocultar un bloque.
  • M-j (Alt + j) : Unir líneas, lo contrario de un Enter que puede separarlas.
  • s-º (Meta + º) : Activa el modo focus.
  • M-s-h (Alt + Meta + h) : Resalta o elimina el resaltado del símbolo bajo el cursor.
  • M-RET (Alt + Enter) : Selección rectangular CUA.

Teclas para trabajar con varios cursores a la vez

Los cursores múltiples nos permiten editar varios lugares dentro de un buffer. Son muy útiles cuando insertamos o eliminamos trazas, cuando tenemos que escribir o borrar el mismo texto en varias regiones del código sin tener que ir al buscar y reemplazar varias veces o simplemente cuando queremos sorprender a las visitas o a los que nos miran por encima del hombro cuando estamos trabajando.
En mi configuración de Emacs podemos utilizar las siguientes teclas (algunas de ellas sacadas de la página oficial de la extensión multiple-cursors de Emacs).
Primero debemos seleccionar una región, puede ser una palabra o un fragmento, ya lo hagamos con el ratón o con C-SPC. Luego pulsamos:

  • C-
  • C-> o C-+ (Control + > o Control + “+”) : Para crear un cursor en la siguiente réplica al texto seleccionado.
  • C-c C-

También podemos colocar cursores con el ratón si pulsamos s- o lo que es lo mismo la tecla meta o tecla Windows y luego hacer click en algún punto del buffer. Cuando tengamos los cursores podemos escribir cualquier texto y se replicará en cada uno de los cursores, y para insertar una nueva línea podemos pulsar C-j. Los cursores desaparecerán al pulsar Enter o C-g.

Otras opciones que tenemos disponibles son:

  • C-c c r (Control+c, c, r): Para seleccionar regiones rectangulares.
  • C-c c c : Editar las líneas de la región

Foto principal: unsplash-logoCaspar Rubin

The post Creando una configuración personalizada para Emacs. Mi .emacs.d con explicaciones detalladas (II – Edición de código) appeared first on Poesía Binaria.

Adrianistán: Leer y escribir JSON en Rust con Serde

$
0
0

JSON es un formato muy popular para guardar y transmitir información. En Rust podemos leer y escribir JSON de forma transparente gracias a Serde. Serde es una librería para Rust, que permite transformar las structs nativas de Rust en ficheros JSON, YAML, BSON, MsgPack, XML y viceversa. Serde está compuesto de varios plugins, uno para cada formato de archivo, así que vamos a necesitar el plugin de JSON. Es lo que se dice serializar (guardar) y deserializar (leer).

Deserializando JSON con Serde

Para deserializar necesitamos primero definir el struct en Rust que se corresponde al JSON que vamos a cargar. Además, hay que incluir una cláusula derive e indicar que derivamos Deserializable.

#[derive(Deserialize)]
struct Landmark {
    x: i32,
    y: i32,
    name: String
}

Una vez hecho eso podemos realizar la transformación con Serde. Serde admite varios métodos de entrada. En mi caso voy a usar from_reader, que permite transformar desde cualquier std::io::Read. También se podría usar from_str, que permite leer desde un String.

Así pues un programa que lea Landmarks de un fichero JSON como este:

[{"x": 42,"y" : -1,"name" : "Presa de Tibi"
},
{"x" : 50,"y" : 23,"name" : "Rollo de Villalón de Campos"
}]

Quedaría así:

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

use std::io::BufReader;
use std::fs::File;

#[derive(Deserialize)]
struct Landmark {
    x: i32,
    y: i32,
    name: String
}

fn main() {
    let file = File::open("landmarks.json").unwrap();
    let reader = BufReader::new(file);
    let landmarks: Vec<Landmark> = serde_json::from_reader(reader).unwrap();
    for landmark in landmarks{
        println!("Landmark name: {}\tPosition: ({},{})",landmark.name,landmark.x,landmark.y);
    }
}

Nótese como usamos Vec<Landmark> para indicar que vamos a cargar un JSON que es un array de Landmarks.

Serializar JSON

Ahora si queremos generar un archivo JSON es muy sencillo. Simplemente marcamos Serialize en la misma estructura que queramos serializar. Posteriormente en Serde podemos utilizar diferentes métodos, como to_writer o to_string.

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

use std::io::{BufReader,BufWriter};
use std::fs::File;

#[derive(Serialize, Deserialize)]
struct Landmark {
    x: i32,
    y: i32,
    name: String
}

fn main() {
    let landmark = Landmark {
        x: 67,
        y: 23,
        name: String::from("Academia de Caballería")
    };
    let output = File::create("caballeria.json").unwrap();
    let writer = BufWriter::new(output);
    serde_json::to_writer(writer,&landmark).unwrap();
}

Personalizando la serialización y la deserialización

Serde permite a través de atributos, definir de forma precisa que ocurre con los elementos. En caso de no poner nada, por ejemplo, Serde usará los nombres del struct de Rust en los ficheros y si hay elementos de más, en el fichero al leer, los ignorará.

Por ejemplo, si queremos que en el fichero JSON, en vez de tener name sea nombre, podemos usar rename.

#[derive(Serialize, Deserialize)]
struct Landmark {
    x: i32,
    y: i32,
    #[serde(rename = "nombre")]
    name: String
}

Podemos también no querer guardar algún elemento del struct, usamos skip.

#[derive(Serialize, Deserialize)]
struct Landmark {
    x: i32,
    y: i32,
    name: String,
    #[serde(skip)]
    id: u32
}

Si queremos que Serde falle en caso de que en el JSON haya más campos de los que hemos definido, usamos deny_unkown_fields.
#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
struct Landmark {
    x: i32,
    y: i32,
    name: String,
}

Y si queremos que el nombre del struct sea distinto al que crea oportuno Serde, podemos redefinirlo también:
#[derive(Serialize, Deserialize)]
#[serde(rename = "landmark")]
struct Landmark {
    x: i32,
    y: i32,
    name: String,
}

Por último, mencionar que Serde permite serializar/deserializar cosas más complejas, con otros structs, con vectores, con HashMap, entre sus elementos,… Lo único que tendrá que pasar es que esas estructuras a su vez sean serializables/deserializables con Serde (es decir, hayan puesto derive(Serialize,Deserialize)).

La entrada Leer y escribir JSON en Rust con Serde aparece primero en Blog - Adrianistan.eu.

Blog Bitix: Hemeroteca #12

$
0
0

Cuarto año de Blog Bitix y en febrero serán siete desde que empecé con El blog del pico.dev. En los últimos seis meses he seguido publicando casi a dos artículos por semana y durante el 2017 ha sido casi siempre así, en este semestre han sido 48 artículos, que juntos con los 39 del primero son 87 durante el año 2017, mi mayor tasa de publicación en un año.

En estas hemerotecas siempre repaso cual es la evolución de visitas e ingresos de AdSense que quizá a alguien más le resulte de interés. En Junio y Julio alcance unas 50K páginas vistas el mayor pico de visitas, aunque en meses posteriores han descendido, al principio lo achacaba a los meses vacacionales de verano pero luego se han mantenido no se si por alguna penalización de Google o algún artículo que recibía muchas visitas y haya descendido en su posicionamiento. En cualquier caso en los últimos meses del año las visitas han tenido una tendencia ascendente, de unas 40K páginas vistas y superior respecto a las de hace una año que comparándolas han sido de 25K en 2016 y de 38K en 2017 un crecimiento de un 85%. No está mal, si el año siguiente fuese igual me daría por muy satisfecho.

En el apartado de ingresos por la publicidad AdSense, algunos artículos patrocinados y enlaces de afiliación también ha estado bien. En estos momentos por AdSense es raro el día que no recibo al menos un euro y algún día excepcional he recibido 8, otros algo menos de 2 y 3, mensualmente ya es habitual que esté en un rango de 30 y 45€. Al final el artículo que publiqué sobre los cursos de formación de KeepCoding han merecido la pena como artículo patrocinado con la compra de dos usuario de tres cursos (aunque la mayoría de gente se registran a los cursos gratuitos), pero esto junto con los artículos patrocinados es más esporádico y por el momento poco habitual para mi.

En alguna ocasión me han comentado que me donarían algo por los artículos que escribo, una posibilidad sería usar Patreon o PayPal, sin embargo, no me convence mucho la donación directa por ser demasiado generosa y he preferido añadir una página con ciertos productos tecnológicos y gamers seleccionados para que si alguien le gusta mis artículos, se acuerda y quiere donarme algo lo haga a través de estos enlaces de afiliación. Al que compra le cuesta lo mismo pero yo recibo una comisión. Esto me recuerda que debería hacer alguna nueva donación a proyectos de software libre y estoy pensando en Lineage OS y Replicant.

Quien crea que es mucho que pruebe a crearse una bitácora, un canal en YouTube o un podcast y verá rápidamente que o se acierta en la temática y se es muy bueno creando contenido que genera interés o primero verá que han de pasar dos años y dedicar una ingente cantidad de horas a crear ese contenido para llegar a estas cifras que tampoco son altas, no en vano muchos de estos proyectos son abandonados al cabo de unos meses, por algo será.

Evolución visitas e ingresos de AdSense en 2017

Pero por otra parte he recibido buenos comentarios sobre lo que escribo en Blog Bitix, por algunas personas vía Twitter y otras en algún comentario que me anima a seguir escribiendo y compartiendo como hasta ahora lo que voy aprendiendo e investigando que algún día espero utilizar en algún trabajo antes de que me jubile o pruebe ser un jardinero o barrendero.

Este semestre me propuse publicar una mayor cantidad de artículos relacionados con el lenguaje de programación Java con motivo de la pasada publicación de Java 9. Pero primero empecé con un par de artículos sobre portlets y Apache Tapestry que en algún momento espero añadirlos en alguna sección del libro PlugIn Tapestry. Por supuesto, varios artículos comentando las novedades más relevantes de Java 9 publicado en septiembre entre ellas la modularidad y otras no menos notables.

Una serie de artículos sobre GraphQL como alternativa a una API REST que tiene algunas ventajas. Aún me queda publicar uno de esta serie, en cuanto se acepte un pull request a raíz de una petición que cree con un problema que me encontré.

De los menos en este semestre están relacionados con el software libre. De GitLab tengo otro par de artículos por publicar para realizar integración continua y mostrando más en detalles su gestión de peticiones, wiki y páginas. Si estas navidades tienes algún momento para ver la televisión en uno de los siguientes artículos encontrarás varias ideas relacionadas con la tecnología.

O de opinión.

Algunos artículos de tema alternativos en la temática principal de este blog pero que también me gusta compartir, comentando el cambio que hice de compañía eléctrica a Holaluz, de caldera a termo eléctrico y la diferencia en la factura entre tener caldera y tener termo eléctrico. Un par de artículos de los robles que he plantado que espero prosperen, publicaré alguno más. También algún artículo de desempaquetado.

Una buena colección de libros técnicos que he leído durante el último trío de años y me han gustado junto con una selección de productos tecnológicos.

Espero seguir escribiendo como hasta ahora en el 2018 si el tiempo del que dispongo me lo permite y continuo con motivación. Por lo pronto son unos cuantos artículos que ya tengo escritos a falta de retocar en el momento que los publique. Puedes aprovechar para repasar esta lista y leer alguno si te lo perdiste, por supuesto si aún no lo has hecho, suscríbete con tu lector preferido al feed a Blog Bitix. Finalmente, ya solo me queda desearos con la siguiente imagen linuxera de Klouner… ¡Buen 2016 2018!

¡Buen 2018!

Poesía Binaria: Utilizando la API de MailRelay para enviar correo masivo a nuestra manera.

$
0
0


A pesar del creciente número de canales de comunicación que tenemos a nuestro alcance, el correo electrónico nunca pasa de moda. Prácticamente todos los usuarios de la red tenemos una o más direcciones de e-mail. Y, seguro que más de uno consulta varias veces al día su correo, o está pendiente de la más mínima notificación para mirarlo. No cabe duda que es una potente herramienta de marketing hoy en día, tanto para mantener informados a nuestros clientes como para captarlos o invitarlos a realizar una compra. Es más, si gestionamos un servicio web, seguramente enviaremos mensajes por correo electrónico a nuestros usuarios cuando se registran o cuando debemos notificarles algo.

Aunque a mí me gusta montarme todo tipo de servicios por mí mismo, el envío de correo suele ser un tema delicado que, cuando se vuelve masivo prefiero contratar un servicio que lo haga por mí. Entre otros motivos porque,

  • El servicio de correo es algo un poco pesado, y si hemos montado algo pequeño, el momento del envío, imaginemos, a 1000 personas, puede llegar a colgarnos el servidor. Si tenemos un servidor de correo dedicado para el correo no tendremos problemas, pero normalmente los canales estándar de envío de correo buscarán virus en los e-mails, pasarán algunos filtros anti spam y tendrán que realizar muchas conexiones y si tenemos nuestro servicio de correo en el mismo sitio, éste puede verse perjudicado también..
  • Al mismo tiempo que se envía mucha información, puede recibirse también mucha información. Es muy común que algún servidor de destino no esté disponible en un momento determinado y tengamos que reintentar el envío, puede que tenga un sistema de listas grises, por lo que los correos sean rechazados temporalmente y sean aceptados después de algún intento; y puede que haya correos que no lleguen por lo que se enviarán los famosos mensajes del subsistema de correo (Mail Subsystem) explicando las posibles razones del fallo. Todo esto genera un tráfico que es inofensivo en el caso de 1, 2, o 10 mensajes, pero puede saturarnos si enviamos 1000 o 10000 mensajes.
  • Los servidores se cabrean. Y es que si enviamos muchos mensajes a un mismo servidor de correo, éste verá que todos vienen de la misma dirección IP y algunos empiezan a rechazar los mensajes automáticamente y, por lo tanto no llegarán.
  • Cuando tenemos un volumen pequeño de envío, de unos pocos miles de correos, el hecho de que un pequeño porcentaje de personas marque como correo no deseado nuestros mensajes es terrible. Y es que, aunque hayan escogido una opción en tu web para ser notificados, aunque les envíes una felicitación navideña o aunque te hayan pedido que les envíes un mensaje, siempre habrá gente que lo marque como no deseado. Al menos ha sido mi experiencia desde hace muchos años. Y basta que un servidor grande de correo crea que somos malos, para que no llegue ningún mensaje y nos cueste mucho trabajo hacer que vuelvan a confiar en nosotros.
  • Tracking de mensajes y gestión de suscripciones. Podemos implementar un servicio de tracking de mensajes nosotros mismos, no es difícil, pero puede llegar a generar mucho tráfico y engordaría. Esto nos daría información de quién abre los mensajes. Otro tema es la suscripción, ya que debemos incluir un enlace para que alguien se pueda dar de baja de nuestra lista de correo. Muchos servicios de envío de mailing masivo gestionan estas cosas automáticamente.

Analizar nuestras necesidades

¿Tengo que enviar muchos mensajes? Si, por ejemplo nuestra web genera poco tráfico de mensajes podemos usar nuestro servidor de correo para enviarlos. ¿Cuánto es poco? Depende de las dimensiones de nuestros servidores, pero en un pequeño servidor en un VPS no me gustaría enviar más de 500 mensajes por día y sobre todo si las direcciones pertenecen al mismo dominio. Por ejemplo, si nuestros destinatarios suelen ser de GMail o de Outlook. Eso sí, si nuestro servicio web está en un servidor compartido reduciría drásticamente ese número a unos 200 o así. Tenemos que tener en cuenta que los proveedores de hosting se toman muy en serio el tema del SPAM, tanto en el caso de los compartidos como de los VPS y algunos dedicados. Así que, si detectan tráfico masivo de este tipo pueden desactivar el servicio de correo, tanto por mantener la reputación de sus direcciones IP como para no saturar los servicios que ofrecen.

Otra de las preguntas clave sería la región para la que vamos a enviar nuestros mensajes. Ya que vamos a utilizar un servicio externo para enviar los mensajes, por un lado, nos interesa que los servidores estén lo más cerca posible del servidor de destino, aunque por ejemplo si los destinatarios son ciudadanos de la Unión Europea, debemos almacenar sus datos en servidores situados en Europa. Y esto se puede extender a contratos con posibles clientes que soliciten que los mensajes se envíen desde servidores en Europa. Es más, en ocasiones nuestros servicios pueden estar en varios lugares, incluyendo EEUU y puede que tengamos un problema.

Si finalmente optamos por enviar los mensajes desde un proveedor externo, debemos ver la forma de comunicarnos con él. En algunos casos puede que nos interese utilizar el mismo protocolo SMTP para el envío, o en otros casos puede que nos sea más útil utilizar una API HTTP (SOAP, REST,…) para comunicarnos con él.

Incluso una vez hemos optado por utilizar un software mailing o una plataforma de envío de correo, puede que nos valga sólo con las herramientas web que nos brindan.

Empresas de envío de correo

Existen muchas empresas que nos permitirán enviar mensajes desde su infraestructura. Hoy centraré el post en MailRelay. Es una empresa que envía correos desde Europa, y nos permite enviar hasta 75000 correos de manera gratuita. Y, aunque nos permite crear boletines de forma interactiva desde el panel de control de la web a mí me gusta curiosear y exprimir estos servicios para cumplir mis necesidades.

A la hora de enviar correos masivos puedo crear scripts que realicen envíos extrayendo datos directamente de mi base de datos, sin necesidad de crear importaciones en el servicio de un tercero, e incluso, si son mails de notificaciones, no pertenecientes a una lista de mailing puedo crear el e-mail directamente en mi servidor con un contenido personalizado y enviarlo a través del servicio de MailRelay.

Además, disponen de servicio SMTP, por lo que podemos configurar nuestro ordenador de casa o nuestro servidor para enviar los correos a través del servidor SMTP de MailRelay y ellos se pelearán con el envío, con los reintentos, rebotes, etc.

Obtener clave API

Una vez nos registramos en MailRelay, y verificar nuestra dirección de correo (preferiblemente para nuestro dominio), nos darán una URL para conectar con nuestro servicio que puede ser http://midominio.ip-zone.com (podemos utilizar HTTPS aunque ellos no nos lo den así), debemos crear una clave API. Para ello, debemos ir a Configuración / Acceso a la API / Generar nueva clave API:

A partir de ahí podemos utilizar una serie de acciones a través de una API HTTP. Y, ya que la API es HTTP, podemos crear ejemplos con cURL desde Bash para realizar pruebas o scripts de envío y, no será muy dificil extenderlo a cualquier lenguaje de programación: PHP, Python, Ruby, C, C++…

Todas las llamadas a la API de MailRelay se harán con la misma dirección base. Esta dirección será https://midominio.ip-zone.com/

Usando la API de MailRelay

Para empezar a interactuar con MailRelay lo primero que tenemos que hacer es autentificarnos en el sistema. Para ello debemos enviar una petición POST a https://midominio.ip-zone.com/ccm/admin/api/version/2/, donde midominio.ip-zone.com nos lo van a mandar en un e-mail anterior. Esa API nos enviará respuestas en XML, aunque adicionalmente podemos añadir “&type=json” a la URL para recibir respuestas en JSON.

Autentificarnos en el sistema

La primera petición que tenemos que enviar para trabajar con la API de MailRelay es nuestra identificación. Para ello, enviamos una petición con los siguientes argumentos. Con esta petición obtendremos la clave API del sistema (si la tenemos anotada nos podemos saltar este paso):

  • function: doAuthentication
  • username: [el usuario que nos enviaron en el mail de confirmación]
  • password: [contraseña correspondiente a ese usuario]

Un ejemplo sería este:

curl “https://mi-direccion-mailrelay.com/ccm/admin/api/version/2/&type=json” -d “function=doAuthentication&username=totakiapi&password=********”
{“status”:1,”data”:”******NUESTRA API KEY*******”}

Si queremos extraer ésta información desde Bash podemos hacerlo utilizando la utilidad jq:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash

USERNAME="[nombre_de_usuario]"
PASSWORD="[password]"
URL="https://mi-direccion-mailrelay.com/ccm/admin/api/version/2/&type=json"

_RESPONSE=$(curl "$URL"-d"function=doAuthentication&username=$USERNAME&password=$PASSWORD")
_STATUS=$(echo$_RESPONSE| jq '.status')
APIKEY=$(echo$_RESPONSE| jq -r'.data')

echo"Status: $_STATUS"
echo"Data: $APIKEY"

Obteniendo información de nuestra cuenta

Antes de proceder a enviar correos, necesitamos información de los buzones que tenemos configurados en nuestra cuenta. Para ello, vamos a hacer peticiones con las funciones getMailboxes y getPackages, dichas peticiones llevarán el nombre de la función y nuestra APIKEY, que como la tenemos en una variable (del ejemplo anterior) voy a utilizarla para no exponerla. Aquí podréis utilizar vuestra API KEY directamente si así lo deseáis.

curl “https://mi-direccion-mailrelay.com/ccm/admin/api/version/2/&type=json” -d “function=getMailboxes&apiKey=$APIKEY”
{
“status”: 1,
“data”: [
{
“id”: “1”,
“mailbox_name”: “[nombre del mailbox]”,
“name”: “[mi nombre]”,
“email”: “[mi correo]”,
“check”: “0”,
“username”: null,
“password”: null,
“hostname”: null,
“imap”: “0”,
“apop”: “0”,
“delete”: “0”,
“enable”: “1”,
“confirmed”: “1”,
“internal”: “0”
}
]
}

De aquí debemos quedarnos con el “id”, que será nuestro MAILBOXID. Podríamos extraerlo con:
1
2
_RESPONSE=$(curl "https://mi-direccion-mailrelay.com/ccm/admin/api/version/2/&type=json"-d"function=getMailboxes&apiKey=$APIKEY")
MAILBOXID=$(echo$_RESPONSE| jq -r'.data[0].id')

GetPackages, obtendría los servicios que tenemos contratados y lo podríamos invocar así (en el ejemplo vemos sólo el plan gratuito, que suele ser el ID=6:

curl “https://mi-direccion-mailrelay.com/ccm/admin/api/version/2/&type=json” -d “function=getPackages&apiKey=$APIKEY” | jq
{
“status”: 1,
“data”: [
{
“id”: “6”,
“contractId”: “1”,
“type”: “email”,
“softLimit”: “15000”,
“hardLimit”: “15000”,
“period”: “1 month”,
“startDate”: “2017-12-31”,
“description”: “Bono de emails”,
“warningDate”: “0000-00-00”,
“hardWarningDate”: “0000-00-00”,
“warningPercentage”: “90”,
“active”: “1”,
“subscribersLimit”: “3000”,
“subscribersLimitDate”: null,
“usage”: {
“startDate”: “2017-12-31 00:00:00”,
“endDate”: “1970-01-01 01:33:37”,
“totalItems”: “15000”,
“sentItems”: 0
}
}
]
}

Y de la misma manera que antes, podemos obtener el ID de la siguiente manera:
1
2
_RESPONSE=$(curl "https://mi-direccion-mailrelay.com/ccm/admin/api/version/2/&type=json"-d"function=getPackages&apiKey=$APIKEY")
PACKAGEID=$(echo$_RESPONSE| jq -r'.data[0].id')

Envío de un correo de ejemplo

Una vez tenemos la clave API, el buzón de correo de envío y el contrato seleccionados, procedemos a enviar un e-mail. En el ejemplo he creado un programa en BASH donde pongo los valores de todas las variables deseadas, aunque APIKEY, MAILBOXID y PACKAGEID ya los tenemos de antes. Tendremos que crear un asunto de mensaje, un mensaje y un listado de direcciones y nombres de destinatarios:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash

URL="https://mi-direccion-mailrelay.com/ccm/admin/api/version/2/&type=json"
APIKEY="**********"
MAILBOXID=1
PACKAGEID=1
SUBJECT="Asundo del mensaje"
HTML="<html><body><h2>Mi primer mensaje</h2>Este es el cuerpo del mensaje</body></html>"
EMAILS_Names=("Destinatario 1""Destinatario 2")
EMAILS_Addresses=("destinatario1@dominio.com""destinatario2@gmail.com")

# Creamos el dato que tenemos que mandar con los destinatarios.
_EMAILSTR=""
for ndx in${!EMAILS_Names[*]}; do _
  EMAILSTR=$(echo"$_EMAILSTR&emails[$ndx][name]=${EMAILS_Names[$ndx]}&emails[$ndx][email]=${EMAILS_Addresses[$ndx]}");
done

#Enviamos el correo
curl "$URL"-d"function=sendMail&apiKey=$APIKEY&subject=$SUBJECT&html=$HTML&mailboxFromId=$MAILBOXID&mailboxReplyId=$MAILBOXID&mailboxReportId=$MAILBOXID&packageId=$PACKAGEID&${_EMAILSTR:1}"| jq

La orden sendMail nos debe enviar también una respuesta dividida en status y data. Cuando todo va bien, status vale 1 y data vale true. Debemos comprobar esos valores para saber si el mensaje se ha enviado bien al sistema de MailRelay. Eso no garantiza que el mensaje haya llegado, sólo que ha sido enviado correctamente a MailRelay. Luego debemos comprobar que efectivamente ha llegado en los reportes de la página web o ver las estadísticas desde la propia API.

Aunque podemos enviar correos a listas completas de correo no voy a contemplarlo en este post

Recibir estadísticas desde la API

Para ver las estadísticas e información sobre los correos enviados tenemos varias funciones:
GetMailingLists: Que nos devolverá todos los mensajes que hemos enviado junto con información sobre el envío, detección de SPAM, rebotes, mensajes enviados, estado y demás. Podemos verlo así:

curl “https://mi-direccion-mailrelay.com/ccm/admin/api/version/2/&type=json” -d “function=getMailingLists&apiKey=$APIKEY” | jq
{
“status”: 1,
“data”: [
{
“id”: “62”,
“subject”: “[Asunto del menssaje]”,
“mailbox_from_id”: “1”,
“mailbox_reply_id”: “1”,
“mailbox_report_id”: “1”,
“groups”: “7”,
“text”: “”,
“html”: “[contenido del mensaje]”,
“attachs”: “a:0:{}”,
“date”: “2017-12-30 17:56:47”,
“created”: “2017-12-30 17:56:47”,
“last_sent”: “2017-12-30 17:58:05”,
“deleted”: “0”,
“status”: “idle”,
“start_sent”: “2017-12-30 17:58:05”,
“send_date”: “2017-12-30 17:56:00”,
“subscribers_total”: 2,
“package_id”: “6”,
“id_mailing_list_folder”: “-3”,
“admin_id”: “0”,
“is_spam”: “0”,
“spam_report”: “Spam detection software, running on the system \”services.mr.ip-zone.com\”,\nhas NOT identified this incoming email as spam.  The original\nmessage has been attached to this so you can view it or label\nsimilar future email.  If you have any questions, see\nthe administrator of that system for details.\n\nContent preview:  Asunto del menssajeHola mundo te mando un mensaje tio Asunto\n   del menssaje Hola mundo te mando un mensaje tio […] \n\nContent analysis details:   (0.4 points, 5.0 required)\n\n pts rule name              description\n---- ---------------------- --------------------------------------------------\n-0.0 NO_RELAYS              Informational: message was not relayed via SMTP\n 0.0 HTML_MESSAGE           BODY: HTML included in message\n 0.3 HTML_IMAGE_ONLY_04     BODY: HTML: images with 0-400 bytes of words\n-0.0 NO_RECEIVED            Informational: message has no Received headers\n 0.0 T_REMOTE_IMAGE         Message contains an external image”,
“analytics_utm_campaign”: “”,
“auth_token”: null,
“notification_url”: null,
“campaign_id”: “7”,
“sent”: 2,
“bounced”: 0
},
…..
]
}

En esta función podremos, adicionalmente utilizar los parámetros startDate, endDate para especificar un rango de fechas con el que filtrar la lista, offset para decir por qué mailing list empezar, porque podemos tener cientos o miles de ellas y count para especificar cuántas mailing lists queremos en la salida.

Tras ello, la siguiente función interesante es getStats que podemos utilizar especificando el ID de la mailing list o no:

curl “https://mi-direccion-mailrelay.com/ccm/admin/api/version/2/&type=json” -d “function=getStats&apiKey=$APIKEY&id=62” | jq
{
“status”: 1,
“data”: {
“impressions”: 0,
“unique_impressions”: false,
“clicks”: 0,
“unique_clicks”: “0”,
“sent”: 2,
“bounced”: 0,
“reported_spam”: 0,
“delivered”: 1,
“optouts”: 0,
“forwarded”: 0,
“ignored”: 1,
“subscribers_total”: 2
}
}

Evitar que nuestros mails caigan como correo no deseado

Pero claro, los e-mails enviados pueden llegar como correo no deseado en ciertos entornos, de hecho deberían si el servidor de correo del receptor está bien configurado. Y es que los servidores no deben permitir que cualquiera envíe correos suplantando el dominio o la identidad de otra persona. Sabemos que el correo no se diseñó en un primer momento para ser seguro en ese aspecto y es muy fácil, de hecho muchos virus se aprovechan de eso, enviar correos en nombre de otra persona/empresa/entidad. Afortunadamente los sistemas antispam modernos suelen eliminar casi todos los correos de ese estilo, aunque siempre se cuela alguno.

Algunas de las medidas que se toman para, al menos se compruebe la autenticidad del dominio son los registros SPF y DKIM. Estos registros implican grosso modo que el propietario del dominio es el propietario del servidor de correo de envío. Son registros DNS que debemos añadir en nuestro dominio. El registro SPF indica qué servidores tienen permiso para enviar correos con mi dominio, especificando las direcciones IP o los nombres de dominio permitidos. El mecanismo DKIM es algo más complicado y crea una firma digital que se complementa con un registro DNS de nuestro dominio de forma que es muy rápido comprobar que el correo es legítimo, pero es muy costoso computacionalmente falsificarlo.

Así que es muy recomendable configurar este par de cosas antes de empezar a utilizar nuestra nueva plataforma de mailing.

Foto principal:
unsplash-logoKelli Stirrett

The post Utilizando la API de MailRelay para enviar correo masivo a nuestra manera. appeared first on Poesía Binaria.

Una sinfonía en C#: Javascript tips: Jugando con strings, interpolación y otras yerbas.

$
0
0

Hace un tiempo (3 años) hablé sobre cómo tener strings multilínea en Javascript, una opción no tan conocida incluso hoy en día.

Bien, con las posteriores versiones del estándar y los navegadores de hoy en día ya contamos con algunas características más avanzadas para el manejo de strings, vamos a ver un poco de qué se trata.

Interpolación de strings

Una de las novedades de ECMA Script 6 2015 son los template string (o string interpolado) muy similar a C#, nos permite tener un texto (template) y que el interprete reemplace ciertas cadenas especiales con variables que están dentro del contexto, por ejemplo

var s = "mundo";
`Hola ${s}`

En este caso la variable s tiene el valor “mundo” y cuando hacemos referencia en la siguiente línea a la variable por nombre escribiéndola con esa sintaxis ${variable} y usando las comillas invertidas igual que el en caso de los strings multilínea.

Por supuesto que podemos hacer referencia a objetos complejos

var o = {"nombre": "Leonado", "twitter": "@leomicheloni"}
`Hola, mi nombre es ${o.nombre} puedes seguirme en twitter en ${o.twitter}`"Hola, mi nombre es Leonado puedes seguirme en twitter en @leomicheloni"

Perfecto, y muy útil para que nuestro código sea más limpio y sea menos propenso a errores, pero hay más.

Qué pasa si hacemos referencia a algo que no existe, por ejemplo a una propiedad que no existe en el objeto, nada, como Javascript es dinámico simplemente nos muestra undefined.

var o = {"nombre": "Leonado"}
`Hola, mi nombre es ${o.nombre} y mi apellido es ${o.apellido}`"Hola, mi nombre es Leonado y mi apellido es undefined"

Perfecto, no problem.

Sin embargo si hacemos referencia a una variable que no existe….

`Hola, mi nombre es ${nombre}`
Uncaught ReferenceError: nombre is not defined
    at :1:23

Pumm! se rompe, es decir se comporta igual que si concatenando el strings a la viaja usanza.

"Hola, mi nombre es " + nombre

Pero hay más

Procesar expresiones

También es posible procesar expresiones dentro de un template string, por ejemplo

`Hola, la suman de 1+2 es ${1+2}`"Hola, la suman de 1+2 es 3"

Por supuesto que podemos sumar elementos de objetos

var o = {a : 1, b: 7};
`La suman de ${o.a} + ${o.b} es ${o.a + o.b}`"La suman de 1 + 7 es 8"

Y listo, no podemos reemplazar una biblioteca de procesamiento de templates como Mustache con esto pero nos permite ahorrar trabajo y evitar errores, además de hacer nuestro código más limpio.

Nos leemos.

Adrianistán: Yew, crea webapps al estilo Angular/React en Rust

$
0
0

Hoy os traigo una librería muy potente y muy útil, ahora que Rust compila a WebAssembly de forma nativa. Se trata de Yew, una librería para diseñar frontends, single-page-applications y en general, una librería que es una alternativa a Angular, React, Vue y Elm.

En particular, Yew se basa en The Elm Architecture, por lo que los usuarios de Elm serán los que encuentren más familiar a Yew.

Yew significa tejo. He aquí uno de los tejos más famosos de Valladolid, en la plaza del Viejo Coso

Instalar cargo-web y el soporte a WebAssembly

Antes de empezar a programar necesitaremos instalar un plugin para Cargo, llamado cargo-web, que nos ayudará en el desarrollo web con Rust. Por otro lado, hace falta instalar el soporte de Rust a WebAssembly. Existen tres opciones actualmente: asmjs-unknown-emscripten, wasm32-unknown-emscripten y wasm32-unknown-unknown. Para los primeras opciones hace falta tener instalado Emscripten. Para la última, no hace falta nada, por lo que es mi opción recomendada. Por otro lado, wasm32 significa WebAssembly y asmjs es asm.js, que es simplemente JavaScript y será compatible con más navegadores.

cargo install cargo-web
rustup target add wasm32-unknown-unknown

The Elm Architecture

Los principios de The Elm Architecture se basan en: modelo, mensajes, actualización y vista.

Para este tutorial vamos a hacer la típica aplicación de una lista donde guardamos notas. Nuestro modelo se va a componer de una lista de tareas, para simplificar, pongamos que una tarea es simplemente un String y un ID. Entonces también nos hará falta almacenar un contador para ir poniendo IDs. También, al tener un campo de texto, nos hará falta una variable para ir almacenando temporalmente lo que escribe el usuario.

struct Model {
    id: u32,
    tasks: Vec<Task>,
    input: String,
}

struct Task{
    content: String,
    id: u32,
}

Lo siguiente es diseñar los mensajes. Los mensajes interactúan con el modelo y desencadenan una actualización de la vista. En esta aplicación solo nos hacen falta dos mensajes: añadir mensaje y borrar mensaje. Pero como tenemos un campo de texto, tenemos que introducir un mensaje Change y siempre viene bien un mensaje que no haga nada.

enum Msg {
    Add,
    Remove(u32),
    Change(String),
    None,
}

Una vez hecho esto pasamos a crear la función update, en la que hacemos distintas cosas según el mensaje que recibamos.

fn update(context: &mut Context<Msg>, model: &mut Model, msg: Msg) {
    match msg {
        Msg::Add => {
            let task = Task{
                content: model.input.clone(),
                id: model.id,
            };
            model.tasks.push(task);
            model.id += 1;
        }
        Msg::Change(content) => {
            model.input = content;
        }
        Msg::Remove(id) => {
            let mut i = 0;
            for task in model.tasks.iter(){
                if task.id == id{
                    break;
                }
                i+=1;
            }
            model.tasks.remove(i);
        }
        _ => {

        }
    }
}

Así pues, si se lanza un mensaje Msg::Add lo que hacemos es copiar el valor de la variable temporal input, crear una nueva tarea con su ID y añadirla a la lista de tareas. Ya está. Yew mágicamente actualizará la página para reflejar que la lista de tareas ha sido modificada. Lo mismo pasa con Remove.

Ahora vamos a las vistas. Una vista es una función que devuelve Html<Msg> y se pueden componer varias funciones así. En nuestro caso, tenemos una vista principal donde se ve un campo de texto y un sitio donde se ejecuta un bucle for con las tareas del modelo. Y a cada tarea se le aplica la vista view_task.

fn view(model: &Model) -> Html<Msg> {
    html! {<div><ul>
            { for model.tasks.iter().map(view_task) }</ul><input type="text", value=&model.input, oninput=|e: InputData| Msg::Change(e.value), onkeypress=|e: KeyData|{
                if e.key == "Enter" {
                    Msg::Add
                }else{
                    Msg::None
                }
            }, /></div>
    }
}

fn view_task(task: &Task) -> Html<Msg>{
    let id = task.id;
    html!{<li><span>{&task.content}</span><button onclick=move |_| Msg::Remove(id),>{format!("X")}</button></li>
    }
}

La macro html! nos permite escribir HTML directamente en Rust, con algunas diferencias (¡prestad atención a las comas!). También nos permite introducir código Rust (entre llaves) y closures (observad onclick, oninput y onkeypress).

Finalmente en el método main, inicializamos el modelo y llamamos a program, que empieza a ejecutar Yew.

El código final queda así.

#[macro_use]
extern crate yew;

use yew::html::*;

struct Model {
    id: u32,
    tasks: Vec<Task>,
    input: String,
}

struct Task{
    content: String,
    id: u32,
}

enum Msg {
    Add,
    Remove(u32),
    Change(String),
    None,
}

fn update(context: &mut Context<Msg>, model: &mut Model, msg: Msg) {
    match msg {
        Msg::Add => {
            let task = Task{
                content: model.input.clone(),
                id: model.id,
            };
            model.tasks.push(task);
            model.id += 1;
        }
        Msg::Change(content) => {
            model.input = content;
        }
        Msg::Remove(id) => {
            let mut i = 0;
            for task in model.tasks.iter(){
                if task.id == id{
                    break;
                }
                i+=1;
            }
            model.tasks.remove(i);
        }
        _ => {

        }
    }
}

fn view(model: &Model) -> Html<Msg> {
    html! {<div><ul>
            { for model.tasks.iter().map(view_task) }</ul><input type="text", value=&model.input, oninput=|e: InputData| Msg::Change(e.value), onkeypress=|e: KeyData|{
                if e.key == "Enter" {
                    Msg::Add
                }else{
                    Msg::None
                }
            }, /></div>
    }
}

fn view_task(task: &Task) -> Html<Msg>{
    let id = task.id;
    html!{<li><span>{&task.content}</span><button onclick=move |_| Msg::Remove(id),>{format!("X")}</button></li>
    }
}

fn main() {
    let model = Model {
        id: 0,
        tasks: vec![],
        input: String::from("")
    };
    program(model, update, view);
}

Ejecutando la aplicación web

Usando cargo web, es muy sencillo generar la aplicación web. Simplemente ejecuta:

cargo web start --target-webasm

El resultado, se montará en un mini servidor web. Si accedes a la URL que indica Cargo con tu navegador web, verás algo similar a esto:

Añade items y bórralos. Observa como la aplicación funciona perfectamente.

Distribuyendo la aplicación

Realmente cargo web ha hecho muchas cosas por nosotros. Si nosotros queremos usar Yew en la vida real, no usaremos cargo web. Para ello, compilamos la aplicación web:

cargo web build --target-webasm

Y accedemos a la carpeta target/wasm32-unknown-unknown/release. Allí encontraremos dos archivos que vamos a necesitar. Uno acabado en .js y otro acabado en .wasm. Ambos ficheros deberemos copiarlos donde queramos usarlos. Por último, será necesario un fichero HTML. En el fichero HTML solo hace falta cargar el fichero JS. Yew hará el resto.

Si quieres saber más, puedes descargarte el código de ejemplo que se encuentra en GitHub.

La entrada Yew, crea webapps al estilo Angular/React en Rust aparece primero en Blog - Adrianistan.eu.

Picando Código: GitHub Desktop en Linux

$
0
0

GitHub Desktop LinuxHace un tiempo GitHub publicó GitHub Desktop, una aplicación de escritorio con interfaz visual para trabajar con sus servicios. La versión 1.0 -publicada en setiembre de 2017- fue reimplementada con Electron. El problema es que sólo estaba disponible para Windows y Mac. Electron es un framework JavaScript que permite desarrollar aplicaciones de escritorio con JavaScript, HTML y CSS. Y una de sus ventajas es que permite publicar aplicaciones multiplataforma.

Personalmente no soy muy fan de Electron, no sé si diría que es Flash para escritorio, pero no anda lejos (y sí estoy de acuerdo en lo que dice el artículo: slack es un pequeño programa en JavaScript corriendo en la máquina virtual de otro sistema operativo (Chrome), que hay que ejecutar para esencialmente chatear en IRC). Tampoco soy fan del ecosistema JavaScript. Todo npm y la cantidad de recursos que usan tanto las aplicaciones Electron como un npm install que descarga cientos de paquetes por cualquier proyectito que use, me huelen muy mal. Tampoco usaría GitHub Desktop, me acostumbré a usar git por línea de comando y creo que no aporta a mi flujo de trabajo.

PERO, siendo GitHub un servicio tan popular, con una aplicación que podría ayudar a más gente a acercarse al ecosistema de código abierto, es bueno que esté disponible también en Linux. Cuanto más aplicaciones, menos excusas para no usar el sistema operativo, y más opciones y facilidades para los usuarios.

El pedido de GitHub Desktop para Linux no tardó en llegar. Habiendo trabajado antes en agregar soporte para Linux en otras herramientas, decidí probar qué tan difícil era en Electron. Encontré que ejecutar la aplicación en Linux no llevó tanto trabajo. Con unos pocos commits, ya se podía usar:

GitHub Desktop Linux

 

En el hilo de conversación de GitHub siguieron lloviendo comentarios, de los constructivos y de los otros. Varios usuarios aportaron su conocimiento y código para seguir progresando con el proyecto, y se logró un build estable en Travis. Eventualmente la cosa se formalizó un poco, con este comentario de Brendan Forster (programador en GitHub y uno de los principales desarrolladores del proyecto). A partir de ahora, la mayor parte del trabajo para Linux se va a llevar en el fork shiftkey/desktop. Ahí debemos dar de alta reportes de problemas o pedidos de nuevas características y mandar Pull Requests relacionados con Linux.

Con cada tag nuevo en el repositorio principal de la aplicación, se va a publicar un nuevo release en este fork. De esta forma podemos probar la aplicación sin tener que armar todo el ambiente de desarrollo. Si están interesados en colaborar para que GitHub Desktop tenga una versión oficial para Linux, sigan el fork, y estén atentos a los nuevos releases para probarlos.

GitHub no se ha comprometido a una fecha ni ha destinado demasiados recursos al desarrollo del cliente para Linux todavía. Mencionaron en el reporte inicial que no tenían mucha experiencia en la empresa y probablemente tampoco tenga mucho sentido desde el punto de vista de negocios. Pero están abiertos y alientan a la comunidad a trabajar en conjunto para mejorar el fork hasta eventualmente tener una versión estable.

Veremos cómo progresa el proyecto, y esperemos que gracias a la comunidad podamos contar con una versión oficial para Linux.
https://github.com/shiftkey/desktop


Arragonán: Un repaso a mi 2017

$
0
0

Vuelta al ejercicio de pensar en lo que dio de sí el año, enfrentarlo respecto a los objetivos auto-propuestos en el enero anterior y plantear algunos nuevos para este que acaba de empezar. Así que vamos a ello.

Con Coding Stones trabajamos en un par de productos bastante grandes; o al menos bastante más grandes de lo que podríamos haberlo hecho individualmente como autónomos. Aparte de mucho trabajo en el backend, tuvimos que seguir poniéndonos las pilas en el frontend para tener un flow similar al de back. Durante el año también salieron algunas formaciones y acompañamientos sobre cuestiones metodológicas y temas de arquitectura/testing en frontend. Además a finales de año grabamos varios cursos con los amigos de Codely TV. Así que hemos conseguido la continuidad que nos habíamos marcado.

En OutreachTool hemos conseguido tener una aplicación web que nos ha permitido hacer varias consultorías evitando usar las hojas de cálculo que tan dolorosas resultaban a mis compañeras. La segunda mitad del año nos centramos más en automatizar el delivery final (generación de KPIs como presentación, informe…) que en mejorar la experiencia de uso de la herramienta, así que el reto para este año es ese, que consultores que no estén relacionados directamente con la empresa empiecen a usar la herramienta. Así que objetivo cumplido, pero igual un poco a medias.

Aunque he vuelto a jugar a fútbol, aún sigo arrastrando muchas molestias de mi lesión en el cuádriceps, así que me lo estoy intentando tomar con calma. Sí he conseguido una rutina relativamente buena yendo a entrenar a boxeo, creo que técnicamente se va notando.

Además de organizar el Startup Open Space Zaragoza, estuve en eventos varios: BilboStack/ElComité, T3chfest Leganés, meetup de DevScola, Lechazoconf, UXSpain, Pamplona Software Craftsmanship, TheAntiEvent, Barcelona Software Craftsmanship, Women Tech Makers, Codemotion Madrid… en alguno de ellos contando cosillas sobre Clean Architecture, Jobsket, alternativas a las estimaciones y JS/testing.

También tocaron algunas visitas a Valencia, Madrid y Barcelona por trabajo. Aprovechando una boda, conocí Mérida. Empalmé un par de semanas de vacaciones entremezclando mi pueblo, unos días de playa y una visita a Mallorca donde pude conocer a parte de la comunidad agile balear, así que sí hice vacaciones de verdad.

Donde más he fallado ha sido en los proyectos más personales: para experimentos varios hemos usado Mosica pero nunca me puse en serio en la versión 2, por contra a final de año le he estado dedicando tiempo a otro side-project. Estuve hablando la posibilidad de mentorizar a un dev, pero finalmente no fuimos capaces de darle forma a cómo hacerlo y se terminó diluyendo; sigue siendo algo que me molaría hacer, pero esta vez no me lo voy a plantear como objetivo, y si surge genial.

Y de nuevo, ahí va un popurrí desordenado:

  • He conocido alguna persona que vale muy mucho la pena.
  • Sin seguir siendo un gran lector, volví a leer bastante y variado: Técnicos, relatos, ensayos, filosóficos, novela…
  • En algunos viajes he podido ir viendo a un buen número de amigos y amigas.
  • Seguimos con la buena costumbre de caballeretes de juntarnos casi todas las semanas a cenar.
  • Estuve con varios amigos un finde de Pirineos Sur.
  • Además de nuevo pude pasármelo genial en un buen puñado de conciertos.
  • Vi nacer MulleresTech y disfruté de su primer evento.
  • Han empeorado mis resacas, por eso voy saliendo menos o con el freno más echado.
  • Me invitaron al Calderón y a San Mamés.
  • Una vez más participamos en Movember.
  • De las tripas, sigo parecido.
  • Seguí un curso de escritura creativa… pero no practiqué.
  • Cambié de gimnasio, creo que acertadamente.
  • Volví a ir de escalada.
  • Fui escribiendo en el medium de Coding Stones y muy poco aquí.
  • Volvimos a estar unos días de retiro en Alcañiz.
  • Empecé a trabajar en una nueva versión web del DNDzgz.
  • Este final de año (e inicio de 2018), he estado preparando un pequeño evento con Cachirulo Valley.

Objetivos 2018

  • Afianzar Coding Stones. Después de este año de continuidad, creo que hay que terminar de demostrar la sostenibilidad de nuestro modelo. Tenemos algunos leads muy interesantes, pero hay que concretarlos y ver qué tal siguen saliendo las cosas.
  • Reescribir parte del core de OutreachTool, hay que pagar deuda técnica para poder aumentar la velocidad de desarrollo y soportar mejor los cambios que vamos a introducir. Ahora mismo este es nuestro principal impedimento a nivel de producto.
  • Como comentaba más arriba, mejorar la UX de OutreachTool para hacer que el software lo puedan utilizar consultores externos.
  • Por razones varias terminé el año algo cansado de preparar e impartir charlas, al menos ahora mismo no me apetece nada hacerlo próximamente. Así que para este año me he propuesto dejarlo bastante de lado, quiero escribir más y probar algún formato diferente. Quizás el podcast, llevo ya un par de años pensando en hacer algo.
  • Mejorar, o al menos dar continuidad, la rutina de entrenamiento. Y si empiezan con los interclubes, quizás participar en alguno.
  • Lanzar el nuevo DNDzgz como PWA en el próximo par de meses.

Tengo más ideas que me gustaría llevar a cabo este año, pero no me las quiero poner como objetivos. En realidad son varias cosas que me apetecen mucho hacer pero o bien no quiero meterme ninguna presión, que ya está bien con lo que hay, o dependen principalmente de cuestiones bastante ajenas a mi control.

¡Feliz an nou!

Picando Código: List Categories: Plugin para WordPress

$
0
0

List Categories es un plugin bastante simple para WordPress que permite listar categorías en un post o página con un shortcode:

[categories orderby=count]

Es básicamente una interfaz a través de la API de shortcodes para la función de WordPress wp_list_categories. Cada uno de los parámetros de wp_list_categories se mapea a un parámetro dentro del shortcode del plugin. Por ejemplo en este blog el código [categories child_of=10] (la categoría con ID 10 es software libre) genera lo siguiente:

  • Categorías
  • Dada la popularidad de List Category Posts (mi otro plugin de WordPress), un pedido de ayuda de un usuario me llevó al desarrollo de List Categories. List Category Posts permite listar posts pertenecientes a una categoría con un shortcode, mientras que éste permite listar categorías con el enlace a cada categoría. Dados los términos y los nombre de los plugins, es comprensible un poco de confusión…

    https://github.com/picandocodigo/List-Categories

    El plugin ya lleva unos 3 años de publicado pero por alguna razón nunca había escrito al respecto. Ha tenido varios reviews positivos. Así que si bien es bastante sencillo y usado por poca gente, es bueno saber que a más de una persona le resulta útil y le facilita un poco el desarrollo de blogs con WordPress. No lo he actualizado demasiado, pero mientras no hayan cambios importantes en la función, no sería muy necesario.

    Pueden descargar el plugin desde su panel de administración de WordPress (buscando “List Categories”), o descargarlo desde el sitio web de WordPress. El código fuente está disponible en GitHub (además del repositorio oficial de plugins de WordPress).

    Blog Bitix: Artículo #5 de Yo apoyo al software libre

    $
    0
    0

    La última donación que hice a algún proyecto de software libre fue en mayo del 2017, desde entonces he recibido algunos ingresos por la publicidad AdSense que incluyó en el blog. Los ingresos por AdSense están empezando a ser bastantes constantes de al menos 1€ al día con lo que al mes se convierten en 30€, un més bueno en 50€, con lo que cada dos o tres meses recibo un ingreso.

    También hace unos meses escribí un artículo patrocinado con enlaces de afiliación sobre varios cursos de formación de KeepCoding, la mayoría de usuarios se registran en los cursos gratuitos y ya cuando había perdido las esperanzas de que alguien comprase algún curso pagando un par de usuarios se registraron usando mis códigos de afiliado.

    En esta ocasión los proyectos a los que he hecho una donación han sido LineageOS y Replicant, dos distribuciones de Android que permiten dar una segunda o tercera vida a móviles que desde hace tiempo se han quedado sin soporte oficial del fabricante ni de Google ni parches de seguridad con lo que ello supone en estos dispositivos que se están convirtiendo en cada vez más imprescindibles. Esto genera dispositivos no actualizados, tarde o temprano potencialmente inseguros y la conocida fragmentación en la plataforma Android.

    LineageOSReplicant

    Muchos móviles siguen siendo perfectamente útiles aún pasados más de dos años. En mi móvil, un Samsung Galaxy S3 (i9300) del año 2012 con 4 núcleos y un solo giga de memoria RAM con no tantos años de vida pero que ya podría considerarse obsoleto debido al gran avance en capacidades que experimentado los smartphones usaba la extinta CyanogenMod en su versión 10.2 que incorporaba Android 4.4 (KitKat) cuando de fábrica tenía 4.1 o 4.2 (Jelly Bean). En alguna ocasión he pensado en actualizarlo a alguna versión más reciente de Android aunque por el poco uso que hago del móvil ya me era suficiente con la versión 4.4 de Android, también porque me daba un poco de pereza hacer la actualización aún no siendo demasiado complicado y no tardando mucho sabiendo lo que hay que hacer. En estas dos distribuciones de Android el Samsung Galaxy S3 está soportado.

    LineageOS

    Se trata de la versión heredera de CyanogenMod, soporta una gran cantidad de terminales que con el paso del tiempo va aumentando aunque hasta el momento no he visto que se haya lanzado una versión estable de alguno de ellos, en cualquier caso en el estado actual funcionará bastante bien en cualquiera de los terminales.

    Finalmente, ayer precisamente me decidí a instalar LineageOS 14.1 que incorpora una de las últimas versiones de Android, 7.1, y mucho más reciente que la que tenía, no lo había hecho hasta el momento por eso de no tener una versión estable y no estaba seguro de que tal funcionaría en un móvil tan «antiguo». Después de haberlo hecho he comprobado que funciona de forma perfectamente fluida aún con la poca cantidad de RAM que tiene para los tiempos actuales en los que los móviles ya incorporan entre 2 y 4 GiB, incluso mejor ya que funciona la cámara frontal que antes con CyanogenMod no.

    Se ofrece de forma gratuita pero los servidores que son necesarios para el desarrollo y el mantenimiento de los sitios web tienen unos costes, este es el detalle de los costes de LineageOS.

    CynogenMod 10.2 y LineageOS 14.1

    Replicant

    Es la distribución de Android recomendada por la FSF. Uno de sus objetivos es los teléfonos móviles respeten la privacidad de los usuarios no realizando seguimiento, posicionamiento y no compartiendo datos e incluyendo en el sistema operativo solo elementos libres. Y es que prácticamente todas las aplicaciones quieren acceder a los contactos, fotos, mensajes SMS, llamadas y otros varios permisos que las aplicaciones solicitan y hay que conceder. Esta distribución de Android es para aquellos le dan gran importancia a la seguridad, privacidad y libertad en estos dispositivos que podrían calificarse de seguimiento y vigilancia. Y por ello esta distribución es importante.

    Está basado en LineageOS 13 y Android 6 quitándole los componentes que no mantienen la privacidad y proporcionándole sustitutos. No soporta tantos dispositivos como LineageOS aunque están varios que fueron y son muy populares.

    Replicant 6

    Comprobantes de las donaciones

    En ambos casos las donaciones que he hecho han sido de 15€.

    Donación LineageOS y Replicant

    Donaciones que he realizado hasta el momento:

    • 2015/12: FSFE 40€
    • 2016/09: Wikipedia 10€, Firefox 10€
    • 2017/01: elementary OS 10€, Libre Office 10€, Arch Linux ARM 10€
    • 2017/05: GNOME 15,31€, VideoLAN (VLC) 10€, Arch Linux 0,31€
    • 2018/01: LineageOS 15€, Replicant 15€

    Picando Código: Cómics que estoy leyendo en 2018 – Parte 1: Marvel

    $
    0
    0

    Desde que me mudé a Escocia empecé a seguir algunas series regulares de cómics. Tener tiendas de cómics a la mano donde todos los miércoles se celebra el Día de Cómics Nuevos (New Comic Book Day) hace que parte de mis ingresos sean destinados a la colección de historietas. Ser como un niño con sueldo de adulto es peligroso…

    Como siempre vengo leyendo más que nada Marvel Comics. No soy de los que cree en la rivalidad entre Marvel y DC Comics, simplemente fueron los cómics que más leí cuando empecé y sus personajes tienen un valor mayor para mí. Pero trato de leer de todo un poco para poder disfrutar más cosas. En este primer post comento un poco lo que vengo leyendo y cómo se está desarrollando el universo Marvel desde el año pasado hasta estos días.

    Marvel Generations

    Generations fue una serie limitada de 10 números, cada uno con un equipo creativo distinto. En ella varios personajes clásicos de Marvel arman equipo con sus versiones más modernas. Más que nada basado en gustos personales, compré sólo 5 de las 10: Captain Marvel, Thor, Spider-Man, Hulk y Captain America.

    Marvel Generations

    Carol Danvers (Captain Marvel), Jane Foster (Thor), Miles Morales (Spider-Man), Amadeus Cho (Hulk) y Sam Wilson (Captain America) se enfrentan a sus pares de un pasado donde no los conocen: Mar-Vell, Odinson, Peter Parker, Bruce Banner y Steve Rogers. Destaco Captain Marvel por traer de nuevo a Mar-Vell, el difunto Captain Marvel original que es de los pocos personajes que todavía no ha vuelto a la vida. El número de Hulk me ayudó para conocer el nuevo Totally Awesome Hulk Amadeus Cho. Y todo lo que escriba Jason Aaron de Thor (como es este caso) es generalmente un 10/10.

    Se puede conseguir la colección entera en Amazon versión tapa dura y versión Kindle. Es un buen aporte a la colección y fue un excelente punto de arranque para mí para poder arrancar series nuevas, ya que todo sirvió de introducción a lo que vino después. Generations preparó las cosas para…

    Marvel Legacy

    Marvel Legacy

    Un “relanzamiento” que “no es reboot” de varios títulos de Marvel. La idea fue similar a lo que hizo DC con Rebirth, (término que ya Marvel había usado en los 90’s y no estaría mal recordar un día de éstos en el blog). No he he leído nada de DC Rebirth todavía, pero eventualmente hay varias series que me gustaría revisar. El objetivo con estos relanzamientos es devolver un poco el enfoque a lo que fueron los super héroes centrales de Marvel en su momento, regresando el status quo y reencarnando a algún que otro personaje fallecido (nada nuevo y totalmente esperable, pero todavía no ha vuelto Mar-Vell😢).

    Legacy #1 es un número “autoconclusivo”. Si bien no hay que tener demasiado conocimiento previo de los eventos hasta ahí, abre varias historias en paralelo para los héroes Marvel en esta nueva etapa. Además de algunos títulos nuevos, aparecieron algunos #1 y quedaron los que no cambiaron su numeración. Pero la etiqueta Legacy las definió como buenos puntos de arranque para nuevos lectores. Fue así que me engancharon con varias series 😬
    Se sintió realmente como un redescubrimiento de Marvel Cómics después de estar alejado de las series actuales por un tiempo.

    The Mighty Thor

    Mi regreso a los números mensuales de Thor coincidió con al arranque de Thor: God Of Thunder por Jason Aaron y Esad Ribić en 2012. De las mejores series que he leído y sin duda una de las mejores versiones del dios del trueno. Por suerte Aaron sigue escribiendo a Thor, quien desde 2014 viene siendo Jane Foster. Legacy coincidió con Thor #700, iniciando el arco The Death of The Mighty Thor. Jane Foster tiene cáncer y en el contexto de Legacy, no es arriesgado suponer que el Thor original Odinson volverá a ser digno de llevar a Mjölnir y tener el protagónico del título.

    Voy sólo 3 números de la serie y es de las que sin dudas pienso seguir. La primera parte es un gran número de colección, como no podía ser de otra manera. Con guión de Jason Aaron, participan artistas varios incluyendo al clásico Walter Simonson. Es a la vez un homenaje al pasado y como buen número introductorio, prepara las cosas para lo que se viene. Los dos siguientes que leí siguen manteniendo un nivel muy bueno y cada uno te deja con ganas de leer el próximo. Si bien se disfrutan individualmente mes a mes, ésta es de las series que también va a valer la pena leer de un tirón cuando esté cerrado el arco. Tanto la historia como el arte en cada número son excelentes.

    The Incredible Hulk

     

    Captain America

    Sam Wilson venía siendo el Captain America hasta que volvió Steve Rogers, que en Secret Empire resulta ser un agente de Hydra pero después resultó que no era tan así. Ahora Steve necesita encontrar su lugar en un mundo que ya no confía en él y le teme. El nombre del escritor fue lo que me convenció de empezar a ver cómo venía la mano con esta serie: Mark Waid, uno de mis autores favoritos.

    Por ahora he leído los 2 primeros números y viene bastante bien. Es de los títulos que más muestra ese sentimiento de volver a las raíces de Legacy. Busca rescatar los valores reales que hacen del Capi un héroe. Un arte caricaturesco y una paleta de colores muy particular le dan gran identidad a esta serie. Las dos historias hasta ahora me han resultado entretenidas y reitero, rescata esa esencia que hace de Steve Rogers un personaje interesante.

    Spider-Man

    Spider-Man fue de los primeros personajes que empecé a leer en cómics, y sigue siendo de mis preferidos. En el universo Marvel actual, conviven más de un arácnido:
    Peter Parker en:
    Amazing Spider-Man por Dan Slott.
    Spider-Man vs. Deadpool por Robbie Thompson.
    Peter Parker: The Spectacular Spider-Man por Chip Zdarsky.
    Miles Morales con su título Spider-Man, y por último, Ben Reilly -el clon de Peter Parker- en The Scarlet Spider por Peter David.

    Me gustaría seguirlas todas, pero no da el bolsillo. Estoy siguiendo la serie principal y la de Zdarsky. En particular me interesa la de Peter David porque es otro autor cuyo material me ha gustado mucho tanto con Hulk como Spider-Man 2099. Spider-Man con Miles Morales es otra serie que me gustaría seguir, pero creo que eventualmente tendría que ponerme a leer todo sobre el personaje a través de colecciones. El único título que no me llama tanto es el crossover con Deadpool.

    The Amazing Spider-Man

    La serie principal a cargo de Slott se pone interesante en Legacy, arrancando en el #789. Escribir este número me hizo recordar que se viene el #800, y pensar que hace poco seguía la serie y compré el #700… Hasta recientemente Peter Parker se había transformado en un nuevo Tony Stark, explotando su capacidad científica para hacerse un empresario exitoso. Por suerte -y acatando la premisa de Legacy-, todo eso quedó en el pasado y Peter vuelve a encontrarse con su mala suerte, pierde su compañía y termina durmiendo en el sofá de Bobbi Morse (Mockingbird). La impresión que me dejó fue el “volvió el Spider-Man de antes”, el evidente objetivo de Marvel con estos relanzamientos.

    Lo malo es que ya arrancaron con los Crossovers. Para seguir la historia actual Venom Inc., tuve que comprar números de Venom. Seguramente pueda leer la historia coleccionando solamente los números de ASM, pero no es lo mismo…

    Peter Parker: The Spectacular Spider-Man

    Me generó mucha expectativa Peter Parker: The Spectacular Spider-Man por Chip Zdarsky, y estoy más que conforme. Además de darle un aire fresco al personaje, es todavía más una vuelta a su esencia. El humor de Zdarsky combina perfectamente con el arácnido, y cuando la cosa se pone seria sabe por dónde agarrar. Arrancó con el #1 pero retomó la numeración antigua en el #297 después de una pateada de tablero en el #6. Otro de mis títulos preferidos, sumamente recomendable. Los primeros 6 números ya están coleccionados en paperback, Kindle y Comixology.

    Avengers

    Una de las series que más he coleccionado y seguido: Avengers. Suelen ser el hilo conductor de los eventos Marvel y dan un panorama de los personajes y eventos que dirigen este universo. Actualmente está escrita por Mark Waid, que como si éste título y Cap fueran poco, también se encarga de Champions: Ms. Marvel, Nova, Spider-Man (Miles), Hulk, Viv Vision y Cíclope (del pasado, no el Scott Summers sorete de la actualidad) en un grupo alternativo a los Avengers. La mención viene a que Legacy arranca con un crossover entre estas dos series: Worlds Collide.

    Avengers, Champions

    Podría decir que no es exactamente el mejor punto de arranque para conocer la dinámica actual de los dos equipos. Si bien el primer número de Avengers me gustó y también me dejó esa idea de “volvió el Marvel de antes”, no me terminó de convencer el arco en general. Es una historia con mucha acción pero parece abarcar mucho y apretar poco. Si bien tiene interacciones y desenlaces interesantes, para ser una historia de Mark Waid y la primera para muchos nuevos lectores, se queda corta. El enemigo principal es High Evolutionary, y parece desperdiciado al estar pasando tanta cosa. Pero voy a esperar a leer el último número para hacer un balance final.

    Parece que el “reboot” real de Avengers arranca este mes con Avengers No Surrender. A partir de enero, se transforma en un título semanal en una saga de 16 números que une los tres títulos Avengers y sus autores en uno sólo: Avengers (Mark Waid), U.S. Avengers (Al Ewing) y Uncanny Avengers (Jim Zub). La protagonizan personajes de las tres series y vengadores del pasado y el presente. Me hace acordar a la tan adorada serie de Busiek y Perez en 1998… Si viene por ahí la mano cuenten conmigo. También se promete el regreso de más de un personaje que los fans vienen pidiendo (cantado Bruce Banner) y uno que ni siquiera sabemos que queremos (¿?). Estare atento a cómo se desarrollan las cosas.

    The Incredible Hulk

    Otro de mis personajes favoritos de Marvel. Por primera vez estoy leyendo al “nuevo” Hulk, Amadeus Cho (antes conocido como Totally Awesome Hulk). Recién voy dos números de este título que vuelve a ser escrito por Greg Pak, quien lleva a Hulk de nuevo a Planet Hulk. Por ahora no creo poder emitir un juicio muy competente al respecto. Me entretuvo leerlo, pero no me gustó como otras tantas cosas que he leído de Hulk. Parece que se quiere poner interesante con el monstruo verde imponiéndose un poco y planteando ese eterno dilema de la doble personalidad. Pero por ahora estoy “esperando a ver si se pone bueno”, con posibilidad de dejarlo si no cambia mucho.

    The Incredible Hulk

    Conclusión

    Además de lo mencionado acá, también he leído alguna que otra cosa de Inhumans. Pero guardo los comentarios para la serie de posts Leyendo a los Inhumanos. También he leído miniseries de otras compañías que iré comentando en otro momento. En la próxima parte de este post les cuento qué he leído de DC y demás. También estoy escribiendo sobre el regreso a un grupo de personajes que nunca me enganchó demasiado: X-Men.

    Continuará…

    Adrianistán: ¿Cómo funcionan Meltdown y Spectre? Explicado con InstaStories

    Blog Bitix: Explicación del fallo de seguridad Meltdown y Spectre en los microprocesadores Intel

    $
    0
    0

    Los procesadores Intel se han visto afectados por un grave error de seguridad debido a que fueron diseñados con ejecución especulativa sin tener algunas consideraciones de seguridad, técnica empleada para aumentar el rendimiento pero que tiene efectos colaterales en la cache que pueden se aprovechados para realizar ataques side-channel con los que leer el contenido de la memoria del kernel, independientemente del sistema operativo utilizado.

    El año 2018 ha empezando haciéndose público uno de los peores bugs de seguridad que afecta a absolutamente todos los procesadores Intel que esta compañía ha fabricado en la última década, denominado Meltdown y su variante Spectre, el error tiene su propio nombre, logotipo y página web. Un error de diseño en los procesadores que solo se puede corregir reemplazando el microprocesador o modificando los sistemas operativos aunque se especula con una pérdida de rendimiento en ciertas cargas de trabajo, entre un 5% y un 30%. El error es tan grave que permite leer a un programa la memoria del núcleo del sistema operativo que debería estar protegida. En la memoria del kernel residen las claves de acceso a sistemas o datos sensibles que obtenidos y utilizados pueden ocasionar graves problemas de seguridad con consecuencias económicas o de acceso no autorizado a información. Este error es tan grave que deja al viejo conocido fallo de la división de los Pentium a la altura de chiste. Hace unos meses por si fuera poco se conocía otro error de seguridad en el Management Engine (ME) de Intel.

    MeltdownSpectre Logotipos de Meltdown y Spectre

    Reemplazar todos los microprocesadores es tremendamente caro además de que primero hay que diseñar y fabricar unos que estén exentos del bug que lleva tiempo, meses o años hasta que estén preparados, por lo que la solución hasta el momento pasa por hacer modificaciones en el software y en los sistemas operativos, compiladores y programas para resolver o mitigar el problema. En el sistema operativo la solución consiste en separar el espacio de direcciones del kernel de la de los programas, sin embargo, cada vez que el microprocesador cambia entre un espacio de direcciones a otro hay una penalización en tiempo por lo que en ciertas cargas de trabajo muy intensivas en las que se cambia frecuentemente de contextos como operaciones de red, de almacenamiento rápido o E/S el rendimiento se ve afectado. Para un usuario doméstico, ofimático o juegos la perdida de rendimiento será insignificante y no será apreciable ya que en estos casos el microprocesador no trabaja a la máxima carga o no está cambiando frecuentemente del espacio de direcciones del kernel al de usuario. En grandes centros de datos como la computación en la nube de Amazon Web Services, Google Cloud Platform o Microsoft Azure el rendimiento será más apreciable.

    Como usuarios domésticos para estar protegidos conviene descargar únicamente software de fuentes confiables pero para los usuarios empresariales con sus servicios en la nube en donde los sistemas están aislados pero usando infraestructura compartida y con el descubrimiento de este bug es más grave si no se parchea, los proveedores de infraestructura en la nube ya han planificando tareas de mantenimiento y reinicios obligatorios.

    IntelAMDARM

    Técnicas para aumentar el rendimiento

    Los microprocesadores modernos implementan varias técnicas para aumentar el rendimiento. Una de las mas simples es aumentar la frecuencia de trabajo del microprocesador, uno de los primeros Pentium trabajaba únicamente a 100 Mhz y los actuales llegan hasta los 3 Ghz, casi 30 veces más. Pero aumentar la frecuencia solo es posible hasta cierto límite a partir del cual el microprocesador se calienta mucho y consume mucha energía. Por lo que hay que emplear otras técnicas al mismo tiempo.

    Otra de las mas simples es reducir el tamaño de los transistores, unos transistores más pequeños hace que sea posible incluir más transistores en el mismo espacio físico para incluir caches de mayor tamaño o nuevas funcionalidades, con más velocidad y con menor consumo de energía. El tamaño de los transistores de los Pentium originales era de 800 nanómetros e incluía 3.1 millones, los Intel Core de octava generación se fabrican a 14 nanómetros incluyendo unos 5000 millones, unas 60 veces más pequeños. Aún así cada vez es más difícil cumplir con la ley de Moore ya que se está llegando a límite físico de los átomos de los materiales, consistía en que cada dos años se duplica el número de transistores de un microprocesador.

    Con la ayuda de unos transistores más pequeños y más espacio se aprovecha para aumentar el rendimiento incluyendo más núcleos de cómputo. Pero para aumentar el rendimiento de un núcleo de cómputo individual o el IPC se emplean otras técnicas como utilizar múltiples pipelines para ejecutar varias instrucciones simultáneamente, ejecución fuera de orden para reorganizar las instrucciones y la ejecución especulativa para mantener llenos esos pipelines.

    Escalar

    En un microprocesador escalar se ejecuta una instrucción por ciclo, por ejemplo, en esta secuencia de instrucciones que realizan unas sumas se tardarían 6 ciclos de reloj. A estos microprocesadores que ejecutan una instrucción por ciclo de reloj se les denomina escalares, siendo ejemplos el Intel 486 y el ARM1176 usado en la Raspberry Pi 1.

    Superescalar

    En un microprocesador con dos pipelines o superescalar se pueden realizar varias operaciones simultáneamente, es decir, mientras se realiza la primera operación en la variable m se realiza al mismo tiempo la segunda operación de n, con lo que estas operaciones podrían completarse en únicamente tres ciclos de reloj con la siguiente equivalencia de programa. Ejemplos de microprocesadores superescalares son el Intel Pentium y los ARM Cortex-A7 y Cortex-A53 estos últimos usados en la Raspberry Pi 2 y 3 respectivamente.

    Sin embargo, hacer la suma de o y x al mismo tiempo no es posible ya que antes de calcular x hay que calcular o debido a que uno de los operandos en la suma de x es o, es decir, hay una dependencia en estas instrucciones y se han de ejecutar una después de otra. Con lo que en vez de tres ciclos habría que conformase en ejecutar estas instrucciones en cuatro ciclos.

    Fuera de orden

    Los microprocesadores fuera de orden reordenan las instrucciones de la forma adecuada para que el programa sea equivalente pero manteniendo los pipelines llenos. Cambiando el orden entre las instrucciones x e y se consigue ejecutar las instrucciones en tres ciclos de reloj. Ejemplos de microprocesadores fuera de orden son el Pentium 2 y siguientes microprocesadores Intel y AMD incluyendo varios ARM Cortex-A9, A15, A17 y A57.

    Predicción de salto y ejecución especulativa

    Los programas incluyen saltos con sentencias condicionales if o de bucle. Los microprocesadores tratan de adivinar si una sentencia de salto se producirá o no (con heurísticas y son bastante buenos acertando) para recuperar y tener preparadas las siguientes instrucciones. Mantener los pipelines llenos es difícil al aumentar su número a tres o cuatro. Para tratar de mantenerlos llenos los microprocesadores usan la predicción de salto y van ejecutando las instrucciones desechando las operaciones si finalmente no se acierta en el salto pero habiendo aumentado el rendimiento si se ha acertado, realizan ejecución especulativa de las instrucciones.

    En este otro caso, v depende de u y u depende de t de modo que un microprocesador superescalar sin ejecución especulativa tardará tres ciclos computando t, u y v para determinar el valor de v en la sentencia condicional if (en otro ciclo) momento en que pasa otros tres ciclos calculando w, x e y, en total 4 o 7 ciclos dependiendo de si hay salto en la sentencia condicional.

    Si el predictor de salto determina que es probable que la condición sea cierta la ejecución especulativa reordena el programa de la siguiente manera:

    Y con la ejecución superescalar se mantiene los pipelines ocupados de modo que el ejemplo tiene la siguiente equivalencia y tardando aproximadamente 3 ciclos cuando antes se necesitaban 7.

    Cache

    Los microprocesadores son muy rápidos comparados con la memoria o el acceso al almacenamiento secundario. Un Cortex-A53 de una Raspberry Pi puede ejecutar una instrucción en 0.5 nanosegundos pero el acceso a memoria costar 100 nanosegundos. Esto no es bueno pero por fortuna los accesos a memoria siguen patrones, accediendo repetidamente a variables recientemente accedidas y accediendo a variables en posiciones cercanas, de forma que colocando estas variables en una cache más rápida y cercana al procesador que la memoria principal se mitiga en gran medida el problema.

    Relación entre ejecución especulativa, cache y Meltdown y Spectre

    La ejecución especulativa tiene el efecto colateral de colocar datos en la memoria cache del microprocesador y esto es utilizado para realizar una forma de ataque side-channel. Desde el punto de vista de Meltdown y Spectre y la ejecución especulativa lo importante es que midiendo el tiempo que tarda el acceso a memoria se puede conocer si el dato está en la cache (tarda poco) o no (tarda mucho).

    u tiene una dependencia sobre t y v sobre u con lo que el microprocesador usando la superescalabilidad, la ejecución fuera de orden y ejecución especulativa acabaría transformando el programa en la siguiente secuencia de operaciones:

    El microprocesador lee de el valor de una dirección del kernel de forma especulativa pero el fallo en la operación de acceso no se produce hasta se conoce el valor de v utilizando en la sentencia condicional no es cero. Limpiando la cache previamente y haciendo que v de cero para que no se produzca la excepción con los valores adecuados de las variables (a, b, c, d) la ejecución especulativa de v, y_ = u+d, user_mem[x_] producirá un acceso a la dirección de memoria 0x000 o 0x100 dependiendo del valor del octavo bit recuperado en el acceso ilegal a la dirección de memoria kern_mem[address]. El ataque side-channel se produce midiendo el tiempo que tarda una instrucción posterior que utilice estas direcciones, si está o no está en la cache (por el tiempo que tarda) determina a que dirección de memoria se ha accedido y cual es el valor del octavo bit de una dirección del kernel. ¡Felicidades has leído un bit de la memoria del kernel!. Bit a bit y con tiempo se puede leer todo el contenido de la memoria del kernel aplicando esta operación millones de veces.

    Notas finales

    Los microprocesadores ARM1176, Cortex-A7, and Cortex-A53 usados en la Raspberry Pi no se ven afectados por el Meltdown ya que no poseen ejecución especulativa, los AMD Ryzen tampoco se ven afectados por el Meltdown ya que aunque si soportan ejecución especulativa al contrario de Intel la ejecución especulativa no se permite entre diferentes anillos de seguridad, el kernel se ejecuta en el anillo 0 y las aplicaciones en el anillo 3. Sin embargo, una variante de Meltdown es Spectre que es el mismo caso pero en vez de con la memoria del kernel con la memoria de otra aplicación. Como las aplicaciones se ejecutan en el mismo anillo en este caso los AMD Ryzen y algunos modelos de ARM si se ven afectados por Spectre al igual que también los Intel.

    La ejecución especulativa hace más rápidos los microprocesadores pero habiéndose descubierto este fallo muy inseguros en el caso de los Intel ya que se ve afectados por Meltdown y Spectre a menos que se implementen parches por software ya que por microcódigo no es posible darle solución. Meltdown es más grave pero se puede corregir modificando el kernel aún con una pérdida de rendimiento, Spectre es más difícil de explotar pero más difícil de corregir y lo que se hará en este último caso es mitigar el problema modificando el sistema operativo, compilador y aplicaciones.

    Intel tiene un problema importante, con AMD y sus Ryzen a buen precio, con buen rendimiento y… sin el problema del Meltdown. Para corregir el fallo en el diseño de la arquitectura del hardware Intel va a tener que rediseñar en parte su arquitectura y esto le va a llevar meses hasta tener preparados nuevos modelos de microprocesadores sin el error.

    En el kernel de Linux 4.14.11 ya se han aplicado varios parches al igual que posteriormente se implementarán en Windows y macOS. Yo como usuario de Linux con un Intel Core i5-3210M que posee la característica pcid y esa versión del kernel no he notado ninguna perdida de rendimiento apreciable.

    Variable not found: Los 17 consejos definitivos que todo desarrollador debería seguir para triunfar en el mundo del software

    $
    0
    0
    Aunque por aquellos tiempos ya llevaba bastante tiempo enganchado al desarrollo de software, fue en el año 1992 cuando empecé mi andadura profesional en este mundillo, y he de decir que estos más de veinticinco años han dado para mucho. Poco más o menos, creo que he pasado por todos los roles existentes en el mundo del software, en todas las modalidades laborales posibles: programador, analista, consultor, formador, coordinador de equipos de desarrollo, CTO, empleado, empresario, freelance

    Aparte de muchas alegrías y algún que otro disgusto, lo que tengo claro es que esta trayectoria me ha dado una visión bastante amplia de cómo funciona el mundo del desarrollo de software y las personas que trabajamos en él.

    WinnerComo guardarme estos conocimientos me parecía demasiado egoísta, he decidido compartir con todos vosotros los que considero que son los diecisiete consejos definitivos que debéis seguir si queréis triunfar en el mundo del desarrollo de software.

    Por lo que he ido aprendiendo estos años, seguir estas reglas os llevará a conservar indefinidamente vuestros empleos o clientes, aumentaréis vuestro valor en el mercado, mejoraréis salarios y vuestro grado de felicidad y satisfacción personal crecerá hasta límites insospechados.

    He de decir que, antes de compartirlos con todos vosotros, varias personas ya los han seguido y sus vidas profesionales han mejorado considerablemente. Por ejemplo, Juan M. R. trabajaba como programador junior en una conocida cárnica hace 6 meses y hoy dirige el equipo técnico en una startup en San Francisco. También, Nacho G. L. pudo firmar hace poco el contrato fijo con el que soñaba, incluso con un aumento de sueldo. Rafael P. G. era un programador del montón y ahora es un reputado project manager por el que se pelean las mejores empresas del mundo.
    “Sin los grandes consejos de José María,
    mi vida como desarrollador seguiría siendo un infierno”
    – Ricardo M. C., 2017
    Podéis ser los próximos en dar el salto, sólo depende de vosotros.

    Los 17 consejos definitivos que debes seguir si quieres triunfar en el mundo del desarrollo de software

    1. Protege tu creación

    El sofware que desarrollas es tuyo y así debes sentirlo y protegerlo: como a un hijo. Ofusca el código, introduce nombre de clases, métodos o variables imposibles de entender, y créalas en carpetas, archivos o lugares insospechados del proyecto. Asegura que nadie pueda tocar ese código salvo tú.

    Si lo haces así, estarás gananando un trabajo de por vida, incluso mejor que aprobando unas oposiciones, que seguro que es lo que tu madre siempre quiso para tí. No lo dudes, haz feliz a tu madre.

    2. Aparenta ser inteligente

    La inteligencia es una virtud escasa, y las empresas luchan por tener cerca personas que destaquen en ese aspecto. Crea algoritmos semimágicos que nadie más pueda entender, utiliza abstracciones aunque no sean necesarias, copia código de otros más inteligentes que tú aunque no lo entiendas; pero ojo, sobre todo, nunca atribuyas el mérito a sus autores reales.

    Ya lo dijo Julio César (¡el Romano, no el futbolista!): “además de serlo, hay que parecerlo”. Y en este caso, si no lo eres, al menos aprende a disimularlo.

    3. Aparenta saberlo todo

    Persona inteligente que lo sabe todoEn la línea del punto anterior, el mundo del desarrollo de software está repleto de mediocres. Los reconocerás fácilmente al ser desarrolladores que usan continuamente Google y Stack Overflow porque son tan inútiles que no se saben de memoria el API completo de .NET o no recuerdan cómo se implementa algo tan trivial como el algoritmo de back propagation de una red neuronal.

    Esto te da algunas ventajas porque podrás hacer creer con facilidad que lo sabes todo sólo con que domines un vocabulario básico, un par de palabras clave de moda y sepas emplearlos en los momentos adecuados.

    4. No cedas ante intentos de revisar tu código

    Normalmente son propiciados por personas que no han escrito una línea de código en su vida y sólo persiguen el objetivo de sacar fallos y avergonzarte, sin comprender el contexto o qué motivos han llevado a tenerlo así.

    Porque nuestro código es como el papel higiénico usado: a todo el mundo le parecerá sucio si se lo enseñamos, pero seguro que ellos tampoco tienen una forma más limpia de conseguir el mismo resultado.

    5. Usa los últimos frameworks (y si no los hay que te gusten, los inventas)

    Súbete al carro de los hypes conforme vayan apareciendo, y procura utilizar siempre lo último. Mejorará tu imagen profesional y creará en la empresa una dispersión tecnológica que te hará imprescindible. O si tienes una empresa, estarás consiguiendo clientes cautivos que no podrán sustituirte por otro proveedor con facilidad.

    Si eres programador de frontend, incluso te animo enérgicamente a crear tu propio framework para Javascript. No hay aún suficientes, y crear un marco de trabajo propio te distinguirá del resto de desarrolladores, que son simples ovejas que siguen al rebaño.

    En mi caso, ando desde hace algún tiempo creando AnguilarJS, una evolución personal de AngularJS (Angular+Aguilar=Anguilar, ¿habéis visto qué juego de palabras tan ingenioso? ;D) que en los próximos meses seguro que revolucionará la forma en que desarrollamos en el frontend y se constituirá, esta vez sí, como el framework definitivo.

    6. Cumple siempre las planificaciones, aunque sea a costa de bajar la calidad

    Todo el mundo sabe que es mejor entregar algo que no funcione bien que no entregar nada; tus jefes o clientes sabrán que eres una persona de palabra y sabrán pasar por alto que tal o cual funcionalidad falla. La calidad en el software no es un requisito, sino una característica opcional.

    A tu favor juega el hecho de que todo el mundo asume que los fallos son inherentes al proceso de desarrollo de software.

    7. No dediques el tiempo enseñando o transfiriendo conocimientos a tus compañeros u otros desarrolladores

    Gollum con su tesoroAunque sea poco elegante decirlo tan abiertamente, debes considerar a otros desarrolladores, ya sea de tu empresa o externos, como pura competencia. No dudarán en quitarte el puesto o al cliente en cuanto tengan oportunidad, por lo que no debes alimentar esa posibilidad.

    Sé práctico y guarda tus conocimientos cual Gollum con su Anillo Único.

    8. No inviertas tiempo en automatizar tareas como despliegues o testing

    Es lógico, puesto que estarás favoreciendo que las máquinas algún día nos dejen sin trabajo a todos. Crea arquitecturas complejas en las que realizar este tipo de tareas automáticas sea imposible, o al menos aparente serlo.

    Además de ayudarte a ti mismo, estarás ayudando indirectamente al sector porque no podemos dejar que las máquinas nos sustituyan ni siquiera en este tipo de tareas.

    9. Exige a los demás más que a ti mismo

    Si te exiges mucho a ti mismo en algún momento pondrás al descubierto tus carencias. Evítalo. Es mejor hacerlo con el resto de personas, porque esto te hará destacar sobre ellas.

    Aprende a descubrir el límite de cada colaborador y llévalo hasta él continuamente. Si no aguanta, es que no merece trabajar contigo; haz lo posible por deshacerte de él y sustitúyelo por otro con más energía.

    10. Dedica tiempo a amueblar apropiadamente tu zona de confort

    Hazla tan cómoda que incluso otros desarrolladores quieran entrar en ella a tomar café todos los días; no hay nada como ser anfitrión para ganar el respeto de los compañeros y asegurar tu liderazgo.

    Una vez la construyas, intenta no salir de ella porque en el exterior serás débil y así lo percibirá el mundo.

    11. Las buenas prácticas están sobrevaloradas

    La gente usa principios como SOLID porque creen que introducir esas palabras en su vocabulario habitual les hace ser más cool y aparentar ser mejores profesionales, pero no es así: simplemente lo están usando como escudo para ocultar su propia incompetencia. Los programadores de verdad simplemente crean buen software, no necesitan que ningún purista les diga cómo hacerlo y, mucho menos, seguir prácticas que unos señores, que probablemente no han programado mucho, han dado en considerar como buenas.

    Por tanto, si el acoplamiento es necesario, no dudes en acoplar. Si debes duplicar código, duplica código sin remordimientos de conciencia. Y, por supuesto, optimiza tu código conforme lo vas creando. Sólo los más grandes crean directamente código que araña nanosegundos y exprime al máximo las capacidades de las modernas CPU.

    12. No intentes entender a los usuarios de tus aplicaciones

    Perfect user interfaceTodos sabemos que los usuarios son seres creados por el mismísimo diablo con el único fin de fastidiar a los desarrolladores. Cuando tienes alguna experiencia sabes de sobra cómo deben ser tus aplicaciones; el que no sea capaz de entenderlas quizás no sea merecedor de utilizarlas.

    Y recuerda que las mejores interfaces son aquellas que surjen sobre la marcha, sin mucha reflexión ni estudio previo, sólo usando tu intuición de desarrollador.

    13. Despliega a producción todos los viernes

    No sé de dónde habrá salido la leyenda urbana cuya moraleja es que un sistema jamás debe subirse a producción un viernes. Hazlo y verás qué bien te sientes. Ganarás fines de semana de paz, pues llegarás a ellos con la tranquilidad de haber terminado las tareas semanales y podrás comenzar desde cero el lunes siguiente.

    Y es que no hay nada como el olor de una aplicación compleja desplegada a producción un viernes a las seis de la tarde.

    14. Vuelve al desarrollo en cascada

    Se ha demostrado sobradamente que el agilismo es un mito. Nada confunde más a los clientes que unos plazos inciertos, la ausencia de documentación y entregas continuas de un producto sin acabar.

    Y por no hablar de la pérdida de tiempo que suponen las reuniones periódicas, como las típicas standup daylies de Scrum; en 2016, científicos de la Universidad de Harvard determinaron que un equipo de 6 desarrolladores que se reúnen durante 15 minutos al día, computan un total de 500 horas/hombre al año. ¡Imagina lo que podrías hacer en ese tiempo desaprovechado!

    Las miles de aplicaciones en el mercado creadas usando desarrollo en cascada no pueden estar equivocadas. ¡Desarrolla en cascada como si no hubiera un mañana!

    15. Controla tu propia infraestructura

    Sinceramente, esto de la nube me parece una patraña propiciada por los grandes players del mercado, a los que le va mucha pasta en ello, y no creo que tenga futuro.

    No hay nada más cómodo y fiable que tener junto a tu mesa un servidor en el que alojar tus aplicaciones y servicios. Si quieres escalar, compra un servidor más grande. Si el servidor se cae, vas y lo levantas. Como véis, todas las ventajas de la nube tienen una solución tradicional en el mundo que ahora los hipsters llaman on premise.

    Crea tus aplicaciones para que funcionen bien en esa máquina y no te preocupes por otras cosas misteriosas como las arquitecturas distribuidas, pues seguro que no necesitarás utilizarlas. Y por supuesto, no uses contenedores: son puro hype, nada como una máquina de verdad para ver si las cosas funcionan.

    16. En cuanto puedas, deja de programar

    Seguro que nunca has visto programadores felices, así que lo mejor que puedes hacer es dar el salto a puestos de gestión cuanto antes. Si lo haces, verás que se te da bien porque, por tus conocimientos técnicos, seguro que sabrás exprimir a los demás y obtener el máximo beneficio de su trabajo.

    Nadie con más de treinta y cinco años debería seguir desarrollando. Cuando conozcas a alguien así, piensa en el Principio de Peter: si está ahí es porque ha alcanzado su nivel de incompetencia y no puede ascender más en la jerarquía. Y por esa misma razón, podrás comprobar fácilmente que la mayoría de ellos son auténticos patanes en su trabajo.

    17. Memoriza estos consejos y aplícalos en tu día a día

    Porque si no es así, nada de esto habrá tenido sentido. Debes aprender de memoria estas valiosas recomendaciones y repetirlas diariamente como un mantra. Consigue interiorizarlas de forma que las incluyas en tus pautas de comportamiento diarios.


    [Actualización 07/01/2018] Permitidme el consejo 18: no sigáis los consejos anteriores. Es un post publicado el 28 de diciembre, día de los Santos Inocentes, y básicamente os recomienda que hagáis auténticas barbaridades. Probablemente, aunque no os aseguro nada, os irá bastante mejor si hacéis justo lo contrario ;)



    ¡Y esto es todo! Espero que sepáis valorar estas recomendaciones en su justa medida, porque, si las seguís, os aseguro que os cambiarán la vida.

    Ya me lo agradeceréis ;)

    Publicado en Variable not found,
    Imágenes: Pixabay,

    Koalite: Mis tecnologías del 2017

    $
    0
    0

    Hace tres años, a petición de Alfredo, escribí un resumen de las tecnologías que había usado profesionalmente a lo largo del 2015. El año pasado convertí en tradición ese post con mis tecnologías del 2016, y este año toca seguirla, como me recordaba Paco.

    Como no espero que nadie se lea los posts anteriores, vuelvo a empezar con pequeño recordatorio del contexto en que escribo este post.

    Se trata de repasar las tecnologías que uso profesionalmente, es decir, no incluyo cosas que me puedan gustar más o menos, o parecer más o menos interesantes, pero que no uso en el Mundo Real™. Esto descarta automáticamente considerar este post como una especie de radar tecnológico o similar de esos que les gustan a algunos.

    Además, y esto es importante, me dedico a desarrollar productos, no proyectos. Estos productos son, en su mayoría, desplegados en decenas de miles de clientes de forma intencionadamente manual (a veces los modelos de negocio pueden resultar curiosos). Están pensados para durar años y es importante mantener compatibilidad hacia atrás. Eso implica que las decisiones que tomamos están condicionadas a conseguir y mantener una estabilidad en el tiempo y no podemos andar cambiando de plataforma o reescribiendo aplicaciones en el último framework de moda tanto como nos gustaría.

    Las plataformas sobre las que trabajamos son, básicamente, .NET para aplicaciones de escritorio (Windows Forms) y servidores, y unas cuantas aplicaciones web en formato SPA desarrolladas con Javascript o TypeScript.

    .NET, servidores y escritorio

    La parte de .NET es la que menos cambia. En parte, porque nuestro objetivo a medio plazo es alejarnos al máximo de esta plataforma, y en parte porque es la que supone un mayor coste para actualizar dentro de nuestro parque instalado.

    Seguimos utilizando .NET Framework 3.5 SP1 en muchas aplicaciones, lo que nos limita la adopción de algunas nuevas funcionalidades, pero desplegar una versión nueva del framework en un parque tan disperso como el nuestro no es trivial. Si a eso le unimos que todavía hay muchos equipos ejecutando Windows Embedded POSReady 2009, una versión de Windows XP SP3 (y con soporte extendido hasta 2019, por si alguien se lo pregunta), actualizar a “lo último de lo último” no es sólo una decisión técnica, sino también comercial.

    Dentro del objetivo de alejarnos de .NET este año hemos dado algunos pasos, reescribiendo una de las aplicaciones Windows Forms en TypeScript y lanzando un nuevo desarrollo basado en Java con Sprint Boot, experiencia, por cierto, bastante satisfactoria.

    Para los desarrollos que mantenemos en .NET, que siguen siendo parte fundamental de nuestros productos, mantenemos nuestro clásico stack basado en NHibernate, Castle Windsor, log4net y NUnit. También usamos NancyFX (sobre .NET 4.5) en algún proyecto, aunque tendemos a trabajar directamente con un HttpListener, o incluso un TcpListener + protobuf cuando necesitamos rendimiento extra.

    Donde sí nos hemos permitido un salto hacia adelante ha sido en la parte de base de datos. Tras un montón de años, nuestra base de datos por defecto ha pasado de ser SQL Server Express 2005 a SQL Server Express 2014, fundamentalmente por necesidades de aumentar el tamaño máximo de la base de datos. Aun mantenemos compatibilidad con ambas, pero algo hemos avanzando. Tal vez resulte raro que no hayamos pasado a la versión 2016 o 2017, pero desgraciadamente no tienen soporte para Windows 7, que es el sistema operativo preferido por nuestros clientes.

    Clientes web

    En la parte web tenemos más flexibilidad porque tenemos menos años de desarrollo acumulado que mantener, pero también porque a la hora de desplegar un cliente no necesitas desplegar ningún runtime, así que podemos usar lo que queramos en desarrollo, que a la hora de la verdad es sólo contenido estático servido desde una de nuestras aplicaciones hecha en C# o Java.

    Todavía tenemos una aplicación con AngularJS (versión 1.algo infernal), pero todo lo demás está desarrollado con ReactJS. Las versiones de javascript varían dependiendo de la aplicación, desde ES5 hastas ES2015, y en las últimas dos aplicaciones hemos utilizando TypeScript.

    Empezamos el año pasado a trabajar en serio con TypeScript y de momento seguimos con él. No sólo eso, si no que hemos convertido una de las aplicaciones principales, que era una aplicación de Windows Forms, en una aplicación web desarrollada con React y TypeScript.

    En ese sentido, algunos de los problemos originales que tuvimos con TypeScript, especialmente los relacionados con las versiones de librerías y typings, se han minimizado con el tiempo. Realmente no sabría decir si es porque ahora los typings están más actualizados, porque tienen menos fallos, o porque una vez que montamos todas las dependencias hemos tratado de no tocarlas. Probablmente esto último influya mucho.

    Para compilar las aplicaciones web tenemos una mezcla de tecnologías que reflejan la evolución de las modas en el mundillo del frontend. Empezamos con grunt, tuvimos algún gulp que nos acabamos cargando, mucho script npm sencillo y, en los proyectos con TypeScript, webpack.

    Cuando no usamos webpack, solemos utilizar browserify como bundler, que sigue siendo una opción excelente por su simplicidad y rapidez. Webpack nos viene bien por la gestión completa de recursos (css, imágenes, código, etc.), pero el precio que se paga en complejidad y en lentitud es muy alto. Actualmente hemos llegado a un punto en que para no desesperarmos tenemos quitados incluso los sourcemaps de TypeScript para que la compilación incremental sea medianamente viable tras aplicar todos los trucos habidos y por haber.

    Para la parte de testing en frontend existen muchas estrategias, pero al final a nivel de librerías tiramos de las típicas: jasmine, mocha, chai, protractor y karma (para la parte de angular), nightwatch para tests de extremo a extremo con React. Enzyme, que empezamos a usarlo el año pasado para algunos tests de react, se va cayendo poco a poco (ya lo suponíamos) porque el tipo de test que fomenta no es un tipo de test que nos resulte muy útil.

    A nivel de interfaz de usuario, después de utilizar material-ui y las omnipresentes alternativas basadas en bootstrap, últimamente me siento más inclinado a utilizar soluciones de tipo utility first css. No es que usemos ninguna librería concreta para ello, pero que en un mundo orientado a componentes tiene cada vez más sentido frente al css “semántico”.

    Teniendo en cuenta que usamos React en bastantes aplicaciones y desde hace ya unos años, puede parecer que lógicamente también usamos React Router, del que he escrito bastante, y alguna librería tipo flux o redux, pero lo cierto es que no. En el caso de React Router, acabamos apostando por minimal router, algo razonable teniendo en cuenta que lo diseñamos para nosotros, y en el caso de flux/redux, nos parece que introducen uan complejidad excesiva y un grado de indirección (o desacoplamiento, si prefieres) muy elevado que hace complicado entender procesos simples. No digo que no sean patrones muy útiles en otros escenarios, pero en nuestro caso utilizando prácticas básicas de reutilización de código en react y tratando de mantener las cosas simples nos está yendo razonablemente bien.

    Herramientas varias

    En la sección de herramientas, pocas novedades, por no decir ninguna. Youtrack, Subversión y CruiseControl .NET lanzando scripts de msbuild coordinando todo (compilación .NET, compilación web, todo tipo de tests, documentación, instaladores, etc.). Sí, es todo viejuno, pero seguimos sin tener incentivo suficiente para asumir el coste de migración a otras herramientas más modernas (y que además son las que muchos usamos fuera del trabajo).

    Para .NET, Visual Studio 2013, que es bastante estable, y para todo lo demás, emacs. Seguramente emacs sea uno de los motivos por lo que tampoco hay mucha variedad de editores en mi vida en los últimos años, ya que en lugar de saltar de un editor a otro, cada vez voy conociendo mejor éste y sacándole más partido.

    Conclusión

    Visto este post, y especialmente si se considera también el del año pasado, puede parecer que a nivel profesional seguimos haciendo lo mismo de siempre. Nada más lejos de la realidad. Este año ha supuesto un salto importante en nuestra estrategia de producto que ha implicado resolver un tipo de problemas al que hacía tiempo no nos enfrentábamos.

    El hecho de haber utilizado tecnologías en las que nos sentíamos cómodos y con las que podíamos ser productivos no es más que un reflejo de eso: tratar de sacar el máximo rendimiento a un contexto determinado. A veces resolver problemas complejos no implica utilizar tecnologías novedosas (y mucho más entretenidas) sino, simplemente, ser capaz de buscar una solución adecuada.

    Posts relacionados:

    1. Mis tecnologías del 2015
    2. Mis tecnologías del 2016
    3. Resumen 2017

    Variable not found: Top posts 2017 en Variable not found

    $
    0
    0
    Antes de nada, me gustaría aprovechar este pequeño momento de vuestro tiempo a desearos un gran 2018 repleto de salud, prosperidad, felicidad y mucha diversión, tanto en los aspectos personales como en los profesionales.

    Y ahora, como manda la tradición, dedicaremos este primer post del año a repasar cuáles son los contenidos que más han llamado la atención a los seguidores de Variable Not Found durante este 2017 que acabamos de cerrar.


    Desarrollador volviéndose locoEncabezado la lista, con mucha diferencia respecto a los siguientes gracias a su difusión a través de Menéame, encontramos el post “Adivina, adivinanza: ¿por qué no compila este código?“, un uso abusivo de la amplitud de UTF8 para conseguir una maldad que, bien empleada, podría volver loco a más de uno ;)

    A continuación, tenemos un artículo donde se muestra paso a paso cómo “acceder a IIS Express desde otro equipo de la red local“, un escenario muy frecuente cuando trabajamos en grupo o con varios dispositivos. Aunque es algo sencillo, nunca está de más tener esta información a mano y actualizada a los sistemas operativos y versiones actuales.

    En tercera posición le sigue la solución a un problema muy frecuente, que nos ahorrará valiosos segundos en nuestro día a día mientras probamos o depuramos una aplicación en nuestro equipo local. En “cómo hacer que Chrome ignore certificados no válidos en localhost mientras desarrollamos una aplicación con HTTPS” mostramos cómo evitar el molesto mensaje de Chrome cuando el certificado local no es seguro.

    El post “Funciones locales en C# 7, también consiguió llamar algo la atención, probablemente por lo novedoso de estas nuevas construcciones del lenguaje con el que trabajamos a diario. Estos artículos sobre las características de C# los escribo principalmente para ayudarme a interiorizar estas novedades, pero me alegra saber que también os pueden resultar útiles para estar al día.

    El artículo “¡Hasta la vista, project.json! ¡Hola de nuevo, .csproj!” abría el año pasado echando abajo una de las grandes ideas que había propuesto .NET Core desde sus inicios: la sustitución de los archivos .csproj por una alternativa mucho más ligera y atractiva basada en JSON. Pero el espejismo duró poco, y en la versión 2.0 se solidificó el tooling devolviendo el protagonismo a los tradicionales archivos de proyecto (eso sí, algo renovados y mejorados).

    En sexta posición del ranking encontramos la presentación del esperado curso de ASP.NET Core 2 MVC en CampusMVP, con contenidos actualizados a la última versión del framework, que por fin comenzó a mostrar los signos de estabilidad y madurez que muchos estabais esperando para dar el salto. Como siempre, muchas gracias por el apoyo al gran número de desarrolladores que ya han confiado en él para ponerse al día.

    A continuación, en el artículo “Retargeting y multitargeting en proyectos ASP.NET Core ” tratábamos uno de los temas más desconcertantes a la hora de comenzar un proyecto con ASP.NET Core: ¿Debo usar el target .NET Core? ¿O quizás .NET Framework? ¿Y por qué no los dos al mismo tiempo?

    Otra muestra más de que las novedades de ASP.NET Core han sido grandes protagonistas este año es el artículo “Flushing asíncrono en ASP.NET Core MVC“, donde mostrábamos cómo era posible ir enviando al cliente porciones de página conforme se iban procesando, en lugar de hacerlo esperar para recibirla de forma completa. Aunque con limitaciones, una técnica muy interesante para escenarios donde queremos reducir el TTFB.

    En penúltima posición, un post sobre un “descubrimiento” surgido de un escenario real, y publicado por si podía ayudar a alguien más. Llevaba años usando Fiddler, pero hasta que tuve la necesidad jamás lo vi como una herramienta para jugar con peticiones y respuestas y conseguir hacer cosas impensables. Y a juzgar por los comentarios que me habéis hecho llegar por distintos medios, muchos de vosotros también habéis visto que eso de “Probar scripts en producción sin desplegar nada, y otros usos interesantes de Fiddler dan bastante juego.

    WinnerY entrando casi de refilón en el ranking porque se publicó un par de días antes de acabar el año, mis “17 consejos definitivos que todo desarrollador debería seguir para triunfar en el mundo del software“, años de conocimiento y experiencia concentrados en diecisiete pildorillas que sin duda os cambiarán la vida. O visto de otra forma, otra de las tradicionales travesuras del 28 de diciembre que tanto me divierten cada año ;D

    Espero que este resumen os sea de interés por si os habéis perdido alguno de estos contenidos en su momento o por si queréis volver a revisarlos. Y ahora, ¡vamos a por el nuevo año!

    Publicado en Variable not found.

    Poesía Binaria: El 2017 para Poesía Binaria. Posts, cambios, retos y lenguajes

    $
    0
    0

    2017 blog resumen del añoComo todos los años, hago un resumen de mi año con el blog. Resumiendo cambios, hitos, agradeciendo mucho a mucha gente y contando alguna cosilla curiosa.

    Si el 2016 fue un buen año para el blog, 2017 ha sido un año más de mantenimiento. Aunque el año empezó fuerte, teniendo en marzo dos posts en Menéame. Uno en portada, Edición de Vídeo en GNU/Linux; y otro en portada de la categoría Linux, Aplicaciones Web Serverless. Por suerte, a pesar de los miles de visitas recibidos en cosa de 3 días, el servidor aguantó el tirón bastante bien. No sólo cuentan las visitas desde Menéame, sino que hubo gente que compartió los contenidos por redes sociales y todo, lo cual me hizo mucha ilusión.

    Pero en el terreno personal ha sido un año muy duro y me ha obligado a estar desconectado. He intentado mantener la frecuencia de uno o dos posts semanales, aunque ha habido semanas que no he podido publicar nada. Finalmente, este año he publicado 56 posts nuevos. Y alguno, muy muy largo. También algunos vídeos en Youtube que espero retomar este año.

    El plano económico

    El año pasado, el blog no me costó dinero. Eso fue una buena noticia para mí, después de andar muchos años invirtiendo para mantenerlo. A ver, es mi hobbie, me hace ilusión y me lo paso bien escribiendo, ¿qué más quiero?. Este año tampoco me ha costado dinero, entre algunos posts patrocinados, algunos referidos de DigitalOcean y alguna cosilla más. Pero entre hosting y dominio ya se fue todo.

    De todas formas, de cara a mis visitantes, aunque alguna vez realice algún post patrocinado, suelo llevármelo un poco al terreno de la programación, o administración de sistemas, poner ejemplos de terminal o algún fragmento de código para dar un valor añadido al visitante. No suelo aceptar todos los que me vienen, es más, han sido sólo unos pocos los que he puesto aquí.

    Mudanza de dominio

    Después de muchos años en el dominio totaki.com al que le tengo cariño y fue casa de uno de mis primeros proyectos web que me gustaría retomar algún día. He decidido adquirir un dominio para este blog. Casi desde que nació el blog quise coger el punto com, pero ese casi supuso que no pudiera hacerlo, por un mes o así y, aunque el dominio no tiene nada y me he puesto en contacto con el dueño, no me contesta. Así que, a mitad de 2017 decidí pillar el punto net.

    Como iba siendo un año malo, sabía que de visitas no andaría bien, así que tiré para adelante. Intentando que en lo relativo al SEO no sufriera demasiado. E intentando hacer la migración con el menor tiempo offline. Cerré la web durante media hora y en más o menos hora y media ya estaba todo probado y funcionando, con redirecciones desde el antiguo dominio, algunos plugins que me había resistido a actualizar actualizados y algunos arreglos que se me habían quedado colgados tiempo atrás hechos. Espero escribir pronto un post de cómo hice todo por si a alguien le sirve.

    El caso es que tras la mudanza, y pasadas dos semanas o así las visitas subieron, aunque no todos los agregadores se portaron bien.

    Redes sociales

    Este año no he estado muy activo en redes sociales. Otras veces, publicito los posts en Facebook y en Twitter siempre que salen, o incluso todos los días publico algún post antiguo. Esta vez no he estado tan activo en Facebook, ni Twitter. He publicado varios posts, sobre todo los nuevos, pero tampoco he sido todo lo constante que me gustaría. Y este año no he participado tanto en grupos de Facebook, y se nota mucho en las visitas totales.

    Aunque aquí os pongo las 5 publicaciones más populares en Facebook:

    1. Foto de Debian en mi móvil Android. Por cierto, publiqué un vídeo de la instalación.
    2. Rescatando capturas de pantalla perdidas en el disco duro.
    3. Consejos para endurecer un servidor SSH
    4. ¿Qué debemos tener en cuenta a la hora de montar un servidor?
    5. 20 cosas chulas que podemos hacer en la terminal de Linux

    En Facebook, la página de Poesía Binaria, tenía 818 likes a día 1 de enero de 2017, y 856 likes a día 1 de enero de 2018. Además, se han descolgado 10 personas de la lista (os echaré de menos).

    En lo que respecta a Twitter, tengo muchos proyectos por llevar a cabo y mucho por hacer para terminar de automatizar muchos de los procesos de publicación de posts. Sólo me queda agradeceros a muchos de vosotros la difusión de algunos tweets y posts: @debianhackers, @sysadmit, @aprendizdsys, @almu_hs, @m4r14h, @yoyo308 y @ochobitsunbyte. Y seguro seguro que me dejo a alguien. Tengo pendiente desarrollar alguna herramienta para monitorizarlo, seguro que existen, pero soy de lo que no hay y me encanta programarlo.

    Aún tengo pendiente de publicar un artículo invitado, por mi amigo @startgo_connect. Lo haré pronto, no me olvido. Además, estoy abierto a colaboraciones, tanto de personas que quieran publicar aquí como para publicar como invitado en otros blogs.

    Aunque no es muy siglo XXI, este año, también estrené newsletter. Aunque sólo envié un e-mail, quiero ponerme un poco las pilas con mis 42 suscritos y enviarles contenidos interesantes.

    Lo más visitado

    Aunque muchos optáis por Google Analytics, yo utilizo Piwik. Dentro de lo que cabe, intento no utilizar demasiados servicios externos, aunque sean gratis, quiero que la información me pertenezca. Es cierto que también tengo Adsense, y que Google puede monitorizar del mismo modo las visualizaciones y quedarse con la información que le dé la gana de mi blog, pero bueno, siento que con Piwik tengo más control.
    Por tanto, lo más visitado este año en mi blog ha sido:

    1. Bucles y cursores en MySQL con ejemplos.
    2. Formas de transformar un entero a cadena en C y C++
    3. Introducción a Timer y TimerTask en Java
    4. 9 trucos para manejar cadenas de caracteres en Bash y no morir en el intento.
    5. ¿Qué características necesito para editar vídeo en GNU/Linux?

    Este año, las estadísticas son muy muy variadas. Hay de todo, y está muy repartido.

    Lo más buscado

    Los usuarios, cada vez usan menos el buscador interno. Casi siempre, las búsquedas suelen ser en Google, y éste remite a una página del blog. Pero bueno, hay cerca de 1000 búsquedas en este sistema. Siendo:

    • imagemagick (11%)
    • bmp (4%)
    • bash (3%)
    • memoria compartida (3%). Me ha resultado muy curioso este resultado.
    • magento (3%)

    Además, aunque Google no nos chiva lo que se ha buscado, utilizando las estadísticas de otros sistemas que sí nos lo dicen. Las búsquedas con las que más gente entra son:

    1. amazon
    2. for en bash
    3. poesía binaria
    4. números primos en C
    5. cursores mysql

    Aquí hay variaciones con respecto a años anteriores. Pero está muy bien

    Navegadores más usados

    • Google Chrome (60%) baja 10% con respecto al año pasado.
    • Mozilla Firefox (22%) baja 1% con respecto al año pasado.
    • Chrome Mobile (6%) entra con fuerza. Tal vez el año pasado el sistema no lo diferenciaba.
    • Safari (2%) baja 1% con respecto al año pasado.
    • Opera (2%) sube 1% con respecto al año pasado.

    Aunque creo que es interesante, tal vez el año que viene sí que veamos un cambio en este aspecto. La entrada de Firefox Quantum en nuestras vidas ha sido fuerte. Llevo años utilizando Firefox y Chrome a diario, pero el aumento de rendimiento en Firefox hace que me decante un poco más por éste último.
    Internet Explorer, Edge y otros, andan por el 1%, pero están en la novena y undécima posicion.

    Sistemas Operativos más usados

    • Windows (62%) sube 1% con respecto al año pasado.
    • GNU/Linux (cualquier distribución) (18%) sube 1% con respecto al año pasado
    • Android (9%) baja 1%
    • Mac OSX (5%) se mantiene

    Enlaces entrantes

    Bueno, fueron dos días lo que duró lo más fuerte del efecto Menéame (y no fue demasiado fuerte, he visto efectos más fuertes), pero con eso bastó para ponerse la segunda en referencias, por detrás de Facebook. En este listado, dejando fuera las entradas desde redes sociales como Facebook, Twitter, Google+, Youtube y LinkedIn:

    Y, ¡qué sorpresa! Encontrarme a un amigo como @elblogdeliher por aquí. Este año, al no publicar tantos posts, otros directorios como bloguers, planetacodigo y planetalinux. Y, otra sorpresa más, algunos de mis posts son enlazados desde aulas virtuales, cosa que me saca una sonrisilla tonta y me hace sentir que mis chuletas le valen a alguien más 🙂

    Países

    El hecho de tener algún que otro post patrocinado hace que, centre un poco los contenidos en un país en concreto. En este caso España, y es normal que haya subido el porcentaje. Además, la mayor parte de los países de Hispanoamérica que me visitaban, lo hacían desde Facebook:

    • España (63%)
    • EEUU (17%)
    • México (6%)
    • Argentina (3%)
    • Colombia (2%)

    Lo más comentado

    Empezamos con las consultas a mi WordPress. Aunque no las pondré todas, porque ya tenemos otros sitios donde consultarlas, algunas cambiarán ligeramente, y las pondré aquí. Como la consulta para ver los posts más comentados, que generará enlaces como los siguientes:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    SELECT WPC.comment_post_ID AS Post_id,
         COUNT(WPC.comment_post_ID)AS Comments,
         WPP.post_title AS Title,
         CONCAT('<li><a href="https://poesiabinaria.net/', WPP.post_name,'">', WPP.post_title,'</a></li>')AS`Link`
      FROM wp_comments WPC LEFTJOIN wp_posts WPP ON WPC.comment_post_id=WPP.ID  WHERE
         WPC.comment_date>='2017-01-01'AND
         WPC.comment_date<'2018-01-01'AND
         WPC.comment_approved=1AND
         WPC.comment_type<>'trackback'AND
         WPC.comment_type<>'pingback' 
      GROUPBY Post_id
      ORDERBY Comments DESC
      LIMIT5;

    Otras curiosidades

    Este año se han publicado 56 posts y 3 páginas (1.13 por semana) de los cuales 27 de ellos contienen código y 29 de ellos ejemplos de uso de terminal (con el plugin SimTerm). Además, he modificado 24 posts de años anteriores.

    En total 181 fragmentos de código (79 menos que el año pasado, aunque no he contado los de este post). De los cuales:

    • 45 son de C
    • 28 son de C++
    • 28 son de BASH
    • 20 son de Lisp
    • 18 son de Python
    • 17 son de PHP

    Cada post tiene una media de 1919.7018 (393 palabras más que el año pasado) o unas 14721.4386 letras (3211 letras más que el año pasado), ¡enrollándome aún más que otros años!. También he publicado 386 enlaces (más o menos la mitad que en 2016) de los cuales 204 son internos (182 externos).

    Los posts más largos

    Los posts más cortos

    Este año ha habido algunos posts cortos, sobre todo para comentar fotos de mi Instagram, y algunas pequeñas píldoras que quería publicar sin enrollarme demasiado.

    Retos del 2018

    Han pasado 8 días del año y aquí va el primer post. La media anual, por ahora no va nada bien. Aunque me gustaría llegar a los 150 posts (entre posts y páginas) como me propuse hace dos años. Tengo muchas cosas en el tintero que me gustaría probar y sobre las que me gustaría escribir. Y, aunque el tiempo parece cada vez menor, quiero intentarlo. Aunque a este paso voy a tener que ponerme como reto el número de palabras.

    En 2017 empecé fuerte publicando vídeos en mi canal de Youtube, aunque lo tuve que dejar por problemas personales, así que quiero retomarlo, junto con una pequeña sorpresa.

    Foto principal: unsplash-logoBrigitte Tohm

    The post El 2017 para Poesía Binaria. Posts, cambios, retos y lenguajes appeared first on Poesía Binaria.

    Picando Código: Global Diversity CFP Day – Un día global para la diversidad en las conferencias

    $
    0
    0

    ¿Siempre quisiste dar una charla en una conferencia técnica? Que 2018 sea el año que cumples tu sueño. Global Diversity CFP Day es un evento global que busca alentar y ayudar a gente que quiera iniciarse como orador de conferencias:

    Sábado 3 de febrero de 2018
    El sábado 3 de febrero de 2018 van a haber varios talleres alrededor del mundo animando y aconsejando a oradores novatos a armar su primera propuesta de charla y compartir su perspectiva individual sobre cualquier tema de interés para gente en tecnología.

    Cada taller va a contar con oradores experientes a mano para:

    • Darte una buenvenida cálida e inclusiva en un espacio seguro (ve el Código de Conducta al cual todos las participantes deben adherir)
    • ser tus mentores con tu propuesta
    • proveer consejos para tu charla
    • y compartir su entusiasmo para que logres llegar al escenario

    Temas
    Estos talleres no tienen un lenguaje específico, framework o proceso como foco. Incluso si tu charla no está siquiera relacionada a la tecnología queremos saber de tí! En resumen, si hay algo que querés gritar al mundo, estamos felices de ayudarte a que lo logres independientemente de los límites de la tecnología o las comunidades.

    Se está buscando gente en todo el mundo para organizar más talleres. Ya hay talleres confirmados en Argentina, Brasil y varias ciudaes de África, América, Asia, Europa y Oceanía. ¡Faltan muchas ciudades de América Latina y España!. Si quieren organizar un taller en su ciudad, lean esta información. Y si saben de alguien que pueda interesarle, compartan el enlace al sitio de Global Diversity CFP Day.

    Es una excelente iniciativa por donde se le mire, esperemos que siga creciendo 🙂

    RubyConf Urugay 2014

    Bitácora de Javier Gutiérrez Chamorro (Guti): Parallel Programming Library

    $
    0
    0

    Marco Cantu publicó el interesante artículo Explaining Tasks in Delphi Parallel Library… by Creating too Many Tasks en donde usaba TTask con Delphi para procesar datos en paralelo usando diferentes hilos de ejecución. Entonces decidí profundizar más en TParallel de Embarcadero, el objeto que encapsula las tareas en paralelo dentro de PPL (Parallel Programming Library), […]

    Artículo publicado originalmente en Bitácora de Javier Gutiérrez Chamorro (Guti)

    La entrada Parallel Programming Library aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

    Viewing all 2731 articles
    Browse latest View live