Noticias Weblogs Código

Bitácora de Javier Gutiérrez Chamorro (Guti)

Opera no muestra imágenes de Tinypic

May 05, 2015 04:16 PM

Muchos ya os habréis dado cuenta que los magníficos navegadores web Opera, basados en el antiguo motor Presto, no visualizan bien las imágenes de TinyPic desde hace unas semanas, cosa que antes no daba ningún problema. El problema se aplica a Opera 12.x y anteriores, así como a Opera Mini, y está causado por un problema en la última actualización de TinyPic, que intencionadamente o no, esconde las imágenes a navegadores basados en Presto, de modo que al final, estas no se visualizan. La solución que he creado, se basa en user.js que ha creado Drozdman de Informatics Stuff, pero que por varios motivos no me gustaba por los siguientes motivos: 1) Dependía de jQuery, que lo hace más lento y pesado. 2) No comprobaba posibles condiciones de error. 3) Era demasiado lento al usar innecesariamente expresiones regulares de búsqueda y reemplazo. De ese modo he creado mi propia adaptación, [...]

» Leer más, comentarios, etc...

Picando Código

Usando Por Mi Barrio

May 05, 2015 04:02 PM

Ya he escrito antes de Por Mi Barrio, una aplicación adaptada a Montevideo por DATA que pone al alcance de un clic el poder de hacer un reclamo directo a la Intendencia de Montevideo:

Por Mi Barrio, una plataforma que permite a las personas que viven en Montevideo enviar reportes sobre daños, desperfectos, vandalismo y otros problemas de nuestra ciudad desde su computadora o celular.

Por Mi Barrio se conecta al Sistema Único de Reclamos de la IM, por lo que se asegura que los reportes denunciados van a llegar a la división correspondiente de la comuna y permite recibir notificaciones sobre respuestas de la IM.

Hace un tiempo ingresé un reporte y la experiencia de usar la aplicación es genial. Andando en bicicleta por las calles de Montevideo detecté que hay un semáforo muy difícil de ver yendo por Bvar. España hacia el centro.  Así que decidí estrenar mi usuario en Por Mi Barrio ingresando un reporte.

Reporte en Por Mi Barrio

Para dar de alta el reporte, simplemente di clic en el lugar del mapa donde se encuentra el problema, lo categoricé y agregué una breve descripción. El asunto fue ingresado al sistema de la Intendencia y ya puedo comprobar que hubieron dos inspecciones: de Téc. Jardinero e Ing. agrónomo.

Di de alta el problema en febrero, y todavía no ha sido resuelto. Está el tema de la burocracia de enviar dos inspectores distintos a comprobar que el problema existe (el 23 de marzo fue la primera inspección y el 30 de abril la segunda). Pero todos sabemos que los entes gubernamentales son monstruos burocráticos gigantes y difícilmente se caractericen por ser eficientes.

Lo importante es que tenemos una forma muy sencilla de hacer algo al respecto con los problemas de nuestros barrios, en vez de salir a llorar indignados en las redes sociales que hay un pozo sin arreglar en la calle de enfrente. Así que aprovechémoslo. El sistema implementado por DATA no sólo nos da ese poder, sino que brinda transparencia al proceso de trabajo de la Intendencia respecto a los reportes ingresados.

Una parte nueva y sumamente jugosa de la aplicación son las estadísticas. Ahí podemos ver varias gráficas que muestran la cantidad de reportes ingresados, en progreso y resueltos desglosado en las distintas categorías. Podemos por ejemplo detectar qué área de la intendencia es más/menos eficiente con los reportes ingresados por este sistema. Los datos son abiertos y se pueden descargar para estudiarlos o crear nuevas aplicaciones en base a ellos.

Por Mi Barrio - Estadísticas

Por Mi Barrio está basado en FixMyStreet, una aplicación de My Society que puede ser adaptada a otras ciudades. Así que si querés podés llevarlo a tu ciudad con un poco de trabajo. Y si vivís en Montevideo, evitá el llanto fácil en las redes sociales o la queja de bar cuando ves que algo está roto, sucio, o en falta en tu barrio. Usá Por Mi Barrio.

Pueden mantenerse al tanto de las noticias de DATA en su sitio web o su cuenta de Twitter.

» Leer más, comentarios, etc...

Navegapolis

Agilidad para incautos

May 05, 2015 08:32 AM

vendidoLos "CMMI's" cada vez se llevan menos. Tras 20 años de ayudar a las empresas de programación, no se sabe si a mejorar el software, o a licitar con ventaja en los concursos públicos, han perdido fuelle y terreno frente a la agilidad.

Así que ahora le toca al negocio de la consultoría renovarse, porque el asesorar con "CMMI's" se vende cada vez peor, y la agilidad es sin duda la nueva tendencia. Lo malo es que algunos, o bien no entienden lo que venden, o sólo les preocupa hacer el dibujo de un marco lo suficientemente creíble para engatusar a los clientes que se conforman con aparentar e ir a la moda.

 

agile for phbs

 

Así que van a convertir a la agilidad en otro modelo de procesos, porque gracias a su capacidad de marketing, van a ser muchas las empresas a las que les van a enseñar cómo deben organizar a los equipos autoorganizados, o a las que convencerán de que Scrum tan sólo es un ciclo iterativo que pueden implementar sin dejar de "embau-contratar" con clientes y administraciones como hasta ahora, porque su marco ágil funciona sin esa bobada de la colaboración estrecha entre cliente y desarrolladores, que sólo serviría para destapar información inconveniente, como que le están facturando 7 "recursos senior" y en su proyecto sólo hay dos becarios desmotivados.

Y de cosas como la autonomía y "empowerment" de los equipos, cultura ágil de la organización... mejor ni hablar.

 

» Leer más, comentarios, etc...

Variable not found

Interpolación de cadenas en C# 6, a fondo

May 05, 2015 06:50 AM

C# 6Ya jugando con Visual Studio 2015 y C# 6, estoy comprobando que la interpolación de cadenas es una de las características más interesantes y prácticas de las novedades que presentará la nueva versión del lenguaje, porque no son pocas las ocasiones en las que creamos expresiones concatenando literales de cadena y variables, o que hacemos uso de String.Format() para construir strings más complejos.

Hace unos meses ya adelantamos por aquí sus principales características, aunque aún era algo pronto para poder probar en profundidad esta nueva y esperada feature. Ahora, más avanzado ya su desarrollo, ha llegado el momento de echarle otro vistazo más en profundidad y ver cómo queda finalmente (o casi finalmente, todavía podría cambiar algo!).

En este post vamos a ver rápidamente los puntos principales a tener en cuenta para dominar esta nueva característica que sin duda facilitará nuestro trabajo y nos hará más productivos.

1. Interpolación de cadenas a vista de pájaro

Consiste en introducir valores de variables o expresiones en el interior de cadenas de texto sin usar los tradicionales mecanismos, molestos y propensos a errores, como la concatenación o el formateo con String.Format().

Y como un poco de código vale más que mil palabras, he ahí un ejemplo que creo ilustra bastante bien de qué estamos hablando:

Interpolated strings at a glance

Las cadenas interpoladas comienzan con el prefijo “$”, y pueden aparecer en los mismos lugares que una cadena de caracteres tradicional. Es decir, donde siempre hemos podido introducir una cadena de texto, podremos introducir ahora la nueva construcción $"something".

2. Parámetros en cadenas interpoladas

Cuando el compilador de C# encuentra una cadena precedida por el símbolo “$” entenderá que es una cadena con interpolación, y creará el string resultante sustituyendo las expresiones contenidas entre llaves por su valor evaluado en el ámbito local.

Por ejemplo, en la siguiente porción de código se muestra el uso correcto de la variable local name y de la propiedad estática LastName, mientras que la propiedad age no puede referenciarse desde el método estático Main() por ser un miembro de instancia:

Interpolation parameters must be in scope

Si queremos introducir los caracteres de apertura o cierre de llaves en alguna de estas cadenas, debemos escaparlos introduciéndolos dos veces consecutivas:

Curly braces must be scaped

3. Intellisense, refactorings y errores en compilación

Visual Studio nos ayudará durante la codificación de estas cadenas gracias al soporte completo de intellisense en el interior de las expresiones interpoladas:

Intellisense in string interpolation

También estos parámetros en el interior de las cadenas de texto serán sensibles a refactorizaciones. Por ejemplo, si en el código anterior renombramos la variable name a fullName, el nuevo nombre será introducido también automáticamente en el interior de la cadena:

Interpolated string parameters support refactorings
Y por supuesto, cualquier error a la hora de codificar las expresiones se nos notificará en tiempo de compilación, evitando así problemas posteriores, e incluso Visual Studio nos propondrá posibles soluciones para corregirlo:

Compilation errors in string interpolation

4. Expresiones como parámetros

Aunque la forma habitual de uso será probablemente utilizar directamente variables o propiedades como parámetros en las cadenas interpoladas, en realidad entre la apertura y cierre de las llaves podemos poner cualquier expresión que evalúe a object. En la práctica, cualquier expresión no void valdrá:

Expressions in string interpolation

Por supuesto, podemos incluir todo tipo de expresiones, aunque a veces será necesario el uso de paréntesis:

Complex parameters in interpolated strings

La única condición es que la expresión sea válida y compilable en el punto exacto en el que se produce la interpolación. Si la expresión contiene términos definidos en espacios de nombre distintos al actual, o bien se cualifican por completo o bien se insertan los using necesarios en el archivo de código fuente en el que se encuentra la expresión.

5. Los parámetros pueden ser formateados

Al igual que ocurre en el clásico String.Format(), podemos incluir información de formato en los parámetros que incluimos en las cadenas interpoladas con una sintaxis bastante predecible: simplemente, tras la expresión a evaluar, incluimos el carácter dos puntos “:” y a continuación la cadena de formato que especifica cómo debe presentarse la información.

Las cadenas de formato son las estándar en .NET, las que hemos usado toda la vida en String.Format(). Veamos unos ejemplos:

Standard formatting strings

Opcionalmente podemos indicar también el número de caracteres del resultado insertando antes de la especificación el número separado por una coma, y la alineación del contenido (derecha positivo, izquierda negativo):

Formatting and padding in C# string interpolation

6. Los parámetros pueden ser objetos IFormattable

Si parámetros introducidos en una cadena interpolada son objetos IFormattable, se invocará a su método ToString() para que sea él mismo el que se formatee:

Interpolated strings with IFormattable parameters

7. La cultura por defecto es la del hilo actual

Si vamos a utilizar formateo, este detalle es importante: la cultura aplicable será la activa en el hilo de ejecución actual.

String interpolation culture in C#

Esto tiene bastante sentido porque este comportamiento es consistente con su equivalente utilizando String.Format().

8. Las cadenas interpoladas actúan como strings

En los ejemplos que hemos visto hasta ahora, las cadenas interpoladas eran meros sustitutos de un string clásico. El compilador de C# detecta este escenario y durante el proceso de compilación son transformadas en una llamada al método string.Format() como podemos observar en el siguiente código:

Interpolated strings are just strings

Por cierto, si queréis ver cómo funciona esta transformación por dentro, podéis hacer pruebas en la dirección http://tryroslyn.azurewebsites.net/, un sitio web powered by Roslyn en el que podréis introducir código C# 6.0 y ver el resultado descompilado. Muy interesante y recomendable :)

9. Las cadenas interpoladas también actúan como objetos IFormattable

Si el compilador detecta que estamos usando una cadena interpolada en lugar de un objeto IFormattable, actuará de forma ligeramente diferente, lo que nos da alguna ventaja si queremos intervenir en el proceso de formateo de estas cadenas.

Fijaos en este código, donde pasamos la cadena interpolada a una función cuyo parámetro se espera que sea IFormattable:

Interpolated strings as IFormattable objects

En este caso, el código equivalente generado por Roslyn será el siguiente. Se puede observar que no ha sido transformado en una llamada a String.Format() sino en una invocación a FormattableStringFactory.Create(), que construirá el objeto IFormattable que espera la función ToUpper() vista anteriormente.

Interpolated strings as IFormattable objects

¿Y por qué es interesante esto? Pues porque esta técnica nos permite crear formateadores personalizados de forma relativamente sencilla.

Por ejemplo, podemos usar esta capacidad para crear funciones que nos ayuden a formatear una cadena en una cultura determinada. El siguiente código, un clásico entre los ejemplos de uso de esta característica, muestra cómo podemos usar una función para formatear usando cultura invariante:

Format using invariant culture

Otros ejemplos muy ingeniosos que he visto consisten en utilizar esta característica para construir cadenas que requieren un tratamiento especial, como puede ser la construcción de una URL, o incluso una sentencia SQL. En ambos casos es interesante “retocar” el proceso de formateo de parámetros, en el primer caso para introducir codificación, y en el segundo para evitar efectos no deseados como inyecciones de script. No dejéis de echar un vistazo al blog de Thomas Levesque para verlos.

10. ¡Soporte para Heredoc!

En el primer vistazo que dimos a la interpolación de cadenas en C# 6 ya comenté que no había visto soporte para Heredoc o cadenas verbatim, un escenario en el que esta característica tenía mucha aplicación. Bien, pues esto parece que por fin ha cambiado, y ahora sí que podemos ya implementar cadenas interpoladas con múltiples líneas de texto.

Simplemente tenemos que insertar el carácter $ que caracteriza a las cadenas interpoladas justo antes de la arroba @ que indica el comienzo de la cadena verbatim:

Heredoc & interpolated strings

11. Sólo es syntactic sugar, aplicable en tiempo de compilación

Es importante tener en cuenta que esta característica es principalmente lo que solemos denominar syntactic sugar, es decir, una ayuda sintáctica destinada a endulzar la vida del desarrollador, por lo que se resuelve en tiempo de compilación, antes de ejecutar la aplicación.

No existe, por tanto, ningún tipo de penalización del rendimiento en runtime por usar esta técnica más allá del que encontraríamos al utilizar String.Format().

12. ¿Uso en versiones del framework anteriores a la 4.6?

Si usamos cadenas interpoladas como puros string, comentado en el punto 8 de este post, no tendremos problemas en usar esta característica. Es decir, podremos compilar sin problema nuestra aplicación usando C# 6 aunque el target del proyecto sea un .NET framework anterior al 4.6 y todo funcionará bien.

Sin embargo, sí tendremos problemas cuando tratamos cadenas interpoladas como IFormattable, y la explicación la tenéis un poco más atrás, en un sutil detalle que hemos deslizado cuando hablábamos del tratamiento de cadenas interpoladas como objetos IFormattable. Como decíamos, éstas se transformaban en una llamada a FormattableStringFactory.Create(), lo que obviamente implica que el compilador está asumiendo que esta clase existe, cuando en realidad esto no es así en las versiones anteriores a la 4.6 del framework.

En estos casos, la solución consiste únicamente en crear en el proyecto actual las clases FormattableStringFactory y FormattableString, cuyo código fuente podemos obtener desde Github mediante los enlaces anteriores. Y para los perezosos, existe un paquete Nuget (no oficial) que lo hace, y que podemos instalar así:

PM> Install-Package StringInterpolationBridge

Bueno, y es todo, creo ;) Espero que esta revisión en profundidad de la interpolación de cadenas os sea útil para conocer un poco mejor en qué consisten y cómo podrán ayudarnos en nuestro día a día cuando comencemos a trabajar con C# 6.

Publicado en Variable not found.

» Leer más, comentarios, etc...

Picando Código

Fizz Buzz y Pattern Matching

May 04, 2015 02:00 PM

Fizz Buzz es un “ejercicio de entrevista de trabajo” para programadores:

Escribir un programa que muestra los números del 1 al 100. Pero para múltiplos de tres, mostrar “Fizz” en vez del número y para múltiplos de cinco mostrar “Buzz”. Para números que son múltiplos de ambos mostrar “FizzBuzz”.

Imran Ghory empezó a usarlo para filtrar programadores que no supieran escribir código. Aparentemente hay una cantidad alarmante de desarrolladores y personas recibidas de Ciencias de la Computación que no pueden resolver este ejercicio. Jeff Atwood escribió también sobre el tema en su blog.

Generalmente no estoy muy de acuerdo con estas pruebas de entrevista, soy más partidario de entrevistas con Pair Programming y otros enfoques. Pero de todas formas en este post no me voy a centrar en la capacidad de una persona de resolver problemas de entrevistas de trabajo.

ElixirHace poco empecé a leer el libro Programming Elixir: Functional |> Concurrent |> Pragmatic |> Fun de Dave Thomas. Creo que Elixir se está convirtiendo en mi nuevo Ruby.

Como me pasó con Ruby en su momento, estoy empezando a entrarle porque me resulta divertido y curioso. De a poco estoy leyendo las bases, escribiendo scripts cortos y aprendiendo. Va más de una noche que me quedo hasta la madrugada con mi Kindle por un lado y la laptop con el intérprete de Elixir por el otro, leyendo y probando cosas.

Uno de los conceptos nuevos del que leí fue Pattern Matching, proveniente aparentemente de Erlang (Elixir corre sobre la VM de Erlang). En Elixir, el operador = puede ser usado para asignar valor a variables. Pero se llama “match operator” y si bien sirve para asignar el valor de la derecha a una variable a la izquierda, tiene más una funcionalidad de “matchear” (¿emparejar, coincidir?) lo que hay de cada lado.

Entonces:

iex(3)> a = "Rick Sanchez"
"Rick Sanchez"
iex(4)> "Morty Smith" = a
** (MatchError) no match of right hand side value: "Rick Sanchez"
 
iex(4)> "Rick Sanchez" = a
"Rick Sanchez"

Se vuelve más interesante al momento de usar estructuras de datos:

iex(5)> {a, b, c} = {"Glip Glops", :mr_meeseeks, 128}
{"Glip Glops", :mr_meeseeks, 128}
iex(6)> a
"Glip Glops"
iex(7)> b
:mr_meeseeks
iex(8)> c
128
iex(9)> "Glip Glops" = a
"Glip Glops"
iex(10)> a
"Glip Glops"
iex(11)> {f, d} = {a, b, c}
** (MatchError) no match of right hand side value: {"Glip Glops", :mr_meeseeks, 128}

Al principio me costó entender bien cómo se podría usar, pero creo que es parte natural de adquirir un concepto de un paradigma nuevo. Un ejemplo que me ayudó bastante a ir viendo cómo usar Pattern Matching fue el ejercicio de FizzBuzz que nos hace escribir el libro.

Volviendo a FizzBuzz en sí, casualmente en el último Ruby Weekly salió un artículo FizzBuzz In Too Much Detail que analiza -como el título lo indica- el ejercicio en gran detalle. Les recomiendo leerlo, está muy bueno. Empieza analizando el código repetido de la primera implementación, pasando por los principios de DRY (no te repitas), parametrización, aplicando programación funcional y más. Vale la pena leer todos los pasos hasta llegar a la conclusión.

Un código Ruby para resolver el problema de FizzBuzz sin demasiado análisis o preocupación por el código generado puede ser:

(1..100).each do |i|
  if (i % 3 == 0 && i % 5 == 0)
    puts "FizzBuzz"
  elsif (i % 3 == 0)
    puts "Fizz"
  elsif (i % 5 == 0)
    puts "Buzz"
  else
    puts i
  end
end

Sí, hay código repetido, sí, se podría mejorar, pero es lo suficientemente bueno como para que alguien entienda a golpe de vista qué hace el código y funciona. Leyendo sobre funciones en Programming Elixir, el libro nos plantea dos ejercicios para practicar un poco el funcionamiento del lenguaje:

Escribir una función que tome 3 argumentos, si los primeros dos son 0, devolver “FizzBuzz”. Si el primero es 0, devolver “Fizz”, y si el segundo es 0, devolver “Buzz”. Si no se cumplen éstas condiciones, devolver el tercer argumento.

El objetivo era agarrar el concepto de que una función (anónima en este caso) en Elixir puede tener varios cuerpos.

El código que escribí fue el siguiente:

fizzbuzz = fn
  (0, 0, _) -> "FizzBuzz"
  (0, _, _) -> "Fizz"
  (_, 0, _) -> "Buzz"
  (_, _, a) -> a
end

La variable fizzbuzz está guardando la función anónima que defino a continuación. La variable “_” (guión bajo o underscore) matchea lo que venga. Por eso lo único que me importa en el código en la parte donde defino los parámetros que quiero recibir –(parámetros) -> cuerpo de la función– son los ceros, en los demás casos pongo guiones y en el caso de que no sean ceros, nombro el tercer parámetro para devolverlo.

Estoy haciendo pattern matching de los parámetros que recibe la función con los distintos “cuerpos”. Acá fue donde mi cabeza hizo un clic y dije “para ésto sirve el pattern matching”.

I see your value now

IO.puts "FizzBuzz (0,0,3) = #{fizzbuzz.(0,0,3)}"
IO.puts "FizzBuzz (0,2,3) = #{fizzbuzz.(0,2,3)}"
IO.puts "FizzBuzz (1,0,3) = #{fizzbuzz.(1,0,3)}"
IO.puts "FizzBuzz (1,2,3) = #{fizzbuzz.(1,2,3)}"

Esto imprime como pide el ejercicio lo siguiente:

FizzBuzz (0,0,3) = FizzBuzz
FizzBuzz (0,2,3) = Fizz
FizzBuzz (1,0,3) = Buzz
FizzBuzz (1,2,3) = 3

Ahora la segunda parte nos pide que usemos el operador rem(a, b) que devuelve el resto de la división de los parámetros. Debemos escribir una función que reciba un entero (n) y llame a la función anterior (la que llamé fizzbuzz en mi caso) pasándole rem(n, 3), rem(n, 5) y n. Después llamarla siete veces con los argumentos 10, 11, 12, etc. El resultado es otra solución a FizzBuzz pero sin usar lógica condicional:

nuevo_fizzbuzz = fn(n) ->
  fizzbuzz.(rem(n, 3), rem(n,5), n)
end

Y el resultado:

iex(9)> nuevo_fizzbuzz.(11)
11
iex(10)> nuevo_fizzbuzz.(12)
"Fizz"
iex(11)> nuevo_fizzbuzz.(13)
13
iex(12)> nuevo_fizzbuzz.(14)
14
iex(13)> nuevo_fizzbuzz.(15)
"FizzBuzz"
iex(14)> nuevo_fizzbuzz.(16)
16
iex(15)> nuevo_fizzbuzz.(17)
17
iex(16)> nuevo_fizzbuzz.(18)
"Fizz"

Programming ElixirEsto es apenas una muestra mínima de cómo un ejercicio simple nos puede dar a entender un concepto. En mi caso sirvió, aunque el libro mismo me da a entender que tengo mucho más por aprender al respecto de Pattern Matching y demás conceptos de programación funcional. Compartir por acá me ayuda a aferrar un poco más los conceptos y en una de esas animar a alguien a probar Elixir, o aprender algo nuevo de alguien que esté leyendo esto.

Estoy bastante entretenido con el libro, lo recomiendo como introducción al lenguaje. Recién voy por lo básico: pattern matching, inmutabilidad, las bases de Elixir y funciones anónimas. Pero me viene divirtiendo y los ejercicios prácticos por más que parezcan simples ayudan a aferrar los conceptos.

No sé si Elixir será mi próximo lenguaje de cabecera, pero pienso seguir leyendo y ver si eventualmente empiezo a programar cosas con esta tecnología. En el peor de los casos, me está ayudando a pensar las cosas de otra manera y sólo eso ya es un gran aporte. Si les interesa conseguirlo, visiten el sitio The Pragmatic Bookshelf (disponible en papel y/o digital)

» Leer más, comentarios, etc...

Koalite

Captura de vídeo con HTML5

May 04, 2015 05:06 AM

Las posibilidades que ofrecen las aplicaciones basadas en HTML5 y Javascript cada vez se aproximan más a las de las aplicaciones nativas. Por supuesto, una aplicación nativa siempre podrá conseguir un rendimiento mejor y acceder a APIs más específicas y completas, pero en muchos casos el soporte que encontramos en aplicaciones web es suficiente para lo que necesitamos hacer.

En este post vamos a ver un ejemplo sencillo de cómo capturar vídeo a través de la cámara del equipo (ya sea cámara de fotos, webcam, etc.).

OJO: Ten en cuenta que todas estas APIs están todavía en evolución, por lo que el soporte varía dependiendo del navegador y, posiblemente, si lees este post dentro de unos cuantos meses, haya que hacer algunas adaptaciones en el código.

El origen de todo: getUserMedia

El API básica para acceder a los dispositivos de grabación es navigator.getUserMedia. Irónicamente, según la documentación de Mozilla ya está obsoleta, pero la versión nueva navigator.mediaDevices.getUserMedia sólo está soportada de momento en Firefox, así que por ahora usaremos la versión antigua, que presenta la siguiente forma:

navigator.getUserMedia(constraints, successCallback, errorCallback);

Para variar, es un API asíncrona, por lo que recibirá las dos callbacks de rigor, una invocada cuando se completa la operación con éxito y otra cuando se produce algún error, y además recibe un objeto, constraints que nos permite indicar qué queremos recibir (video, audio o ambos), qué formato queremos, si queremos usar la cámara frontal o trasera, etc.

En su versión más simple, podemos invocar este API así:

navigator.getUserMedia(
  {video: true}, 
  function(stream) {
    // Hacer algo con el stream de video 
  }, 
  function(error) {
    // Procesar el error
  });

Al invocarla, se solicitará al usuario permiso para acceder al dispositivo de grabación de video (por defecto la webcam del PC -si la tiene-, o la cámara frontal del móvil), y en caso afirmativo, se llamará nuestra callback con un stream. Y eso nos lleva al siguiente punto, ¿qué hago con el stream?

De streams a URLs

El stream obtenido a través de getUserMedia es un MediaStream que encapsula una o más pistas de audio o video. Al igual que getUserMedia, forma parte del API de WebRTC y es una especifición no definitiva que, a día de hoy, todavía no está soportada en todos los navegadores.

Aunque MediaStream nos permite hacer bastante cosas con el video, nosotros, de momento, sólo queremos mostrarlo en pantalla, por ejemplo dentro de un elemento <video>, y para ello necesitamos alguna forma de poder referenciarlo a través de una URL para usarlo con src del elemento <video>.

Para obtener una url referenciable a partir del MediaStreamm podemos utilizar la función window.URL.createObjectURL. Esta función puede trabajar sobre varios tipos de objetos, entre ellos File o Blob y nos permite obtener una url “temporal” que representa al objeto. Por ejemplo, usando blobs, podríamos construir una imagen en formato png “byte a byte” en un array, y luego con createObjectURL obtener una URL para usarla en un elemento <img>.

Es importante tener en cuenta que las URLs que creemos que con createObjectURL consumirán recursos (especialmente memoria) que no serán liberados hasta que se descargue la página, por lo que se recomienda liberarlos cuando dejes de utilizarlos invocando la función revokeObjectURL.

Ahora tenemos las piezas necesarias para montar nuestra primera prueba de captura de video:

<!DOCTYPE html>
<html>
  <head>
  </head>

  <body>
    <video autoplay="true"/>
    <script src="index.js"></script>
  </body>
</html>
navigator.getUserMedia = navigator.getUserMedia ||  
                         navigator.webkitGetUserMedia || 
                         navigator.mozGetUserMedia || 
                         navigator.msGetUserMedia;
                         
window.URL = window.URL || 
             window.webkitURL || 
             window.mozURL || 
             window.msURL;

window.addEventListener('load', function() {
  
  navigator.getUserMedia({
      video: true
    }, 
    function(stream) {
      var src = window.URL.createObjectURL(stream),
          video = document.querySelector('video');
      video.src = src;      
    }, 
    function(e) {
      console.log(e);
    });
  
}, false);

Aquí os dejo un ejemplo funcionando para que podáis jugar con él.

Capturando un imagen estática

Una situación bastante frecuente es que queramos capturar una imagen estática del vídeo. En otras palabras, hacer una foto. Esto es bastante sencillo utilizando un elemento <canvas> sobre el que mostrar la imagen.

El código para hacerlo es muy simple:

var button = document.querySelectorAll('button'),
    canvas = document.querySelectorAll('canvas'),
    ctx = canvas.getContext('2d');

button.addEventListener('click', function() {
  ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
}, false);

Os dejo otro ejemplo ejecutable.

En la vida real habría que ajustar el tamaño del canvas en base al tamaño del vídeo para respetar las proporciones de la imagen, cosa que podríamos hacer usando el evento loadeddata del elemento video.

Si quisiéramos enviar la imagen capturada a un servidor, la función toDataURL del objeto canvas nos permite obtener un string en base64 con la imagen que se está mostrando en el canvas en ese momento.

Resumen

Aunque siempre andarán un paso por detrás de las APIs nativas, las APIs que ofrece HTML5 son suficientes para muchos casos y nos permiten crear aplicaciones independientes del dispositivo (que no tanto del navegador, ojo) y fáciles de desplegar (básicamente porque no tienen despliegue).

Con las APIs que hemos visto en este post puedes empezar a jugar con la captura de vídeo y de imágenes estáticas, requisito cada vez más habitual en muchas aplicaciones en la que el usuario necesita añadir información de una forma rápida o visual (por ejemplo, para generar un parte de accidente, registrar un gasto con un ticket de aparcamiento, etc.).

Posts relacionados:

  1. La Vida en un Canvas de HTML5
  2. Empaquetar aplicaciones HTML5 con PhoneGap/Cordova

» Leer más, comentarios, etc...

Poesía Binaria

Conexión segura a un servidor web (HTTPS) en C usando openSSL

May 04, 2015 02:12 AM

Bueno, el código no tiene mucha más explicación. El objetivo es poder conectar a un servidor web a través de un protocolo seguro y recibir datos, pero también que podamos ver si hay algún problema con el certificado, verificar el algoritmo de cifrado y los datos del certificado, verificar los certificados intermedios, etc.

Si ignoramos SSL, lo que tenemos que hacer es conectar con un servidor a través de un puerto determinado (como estamos en web, sería el puerto 80, por defecto), y mandarle una serie de datos:

GET / HTTP/1.1[CRLF]
Host: openssl.org[CRLF]
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0[CRLF]
[CRLF]

Por ejemplo. Siendo CRLF un retorno de carro (CR, o \r) y un salto de línea (LF, o \n). Después de la primera línea donde indicamos método URI y protocolo vendrán una serie de cabeceras que completarán la petición: elementos relativos al cliente como el navegador, el host al que estamos conectando (al final accedemos a una IP, pero puede que en esa IP haya muchas webs dentro, aquí podemos especificarla), información que aceptamos (Accept/Accept-Language/Accept-Encoding…), capacidades del cliente (Connection…), cookies, dirección desde la que has llegado, opciones de caché o cabeceras personalizadas, entre otras. Todo eso, seguido de dos CRLF, indicando que no vamos a dar más información.

El servidor nos responderá con unas cabeceras como estas:

HTTP/1.1 200 OK[CRLF]
Date: Fri,28 Aug 2015 10:10:10[CRLF]
Server: Apache/2.4 PHP/5.4[CRLF]
Content-Type: text/html[CRLF]
[CRLF]

(Pero igual que el navegador puede decir muchas cosas, los servidores también pueden contar su vida). Y tras terminar las cabeceras con dos [CRLF] como antes decíamos, nos mandarán la web en cuestión.

Cuando terminan de enviarnos la página, puede que la conexión se cierre porque ya no hay nada más que decir, o puede que se quede abierta un tiempo prudencial (por si queremos pedir otra cosa. Esto suele ser el Keep-Alive, que también lo vemos en las cabeceras, y seguro que si hemos curioseado con Apache alguna vez lo hemos visto por ahí).

Introduciendo SSL, muy rápidamente

El objetivo es que la conexión esté cifrada punto a punto, es decir, que sólo el origen pueda enviar y sólo el destino pueda recibir no importando los saltos que dé la conexión (porque da muchos), ni quién mire entre medias (man-in-the-middle) tanto si tu conexión es compartida, como si tenemos un vecino espiando la conexión WiFi (que siempre pensamos que a quién le va a interesar, pero el día menos pensado nos han robado los datos). Así que hacemos que tanto el origen (navegador) como el destino (servidor) cifren los datos que envían y que éstos sólo puedan ser descifrados por la otra parte (por lo que nuestro vecino se aburrirá sin saber qué se cuece por la red).

Pero claro, yo me puedo conectar a un servidor sin conocerlo, ¿cómo sé la clave de cifrado? Al empezar la conexión, tanto el navegador como el servidor intercambian claves de cifrado para llevar a cabo el proceso, de modo que la persona que está en medio, sólo podría ver un saludo entre los servidores (sabrá que estamos conectando de forma segura, pero no podrá ver el contenido).

Además, ¿quién me garantiza a mi que mi vecino no ha interceptado la señal y está actuando él de servidor o haciendo de proxy? Es decir, yo me conecto con mi vecino que hace de servidor y él se conecta con el servidor de destino curioseando mis mensajes. Ahí entran los datos del certificado (¿con quién estoy conectando? Bueno, se puede falsificar) y las autoridades certificadoras (CA), son certificados que tenemos en nuestro equipo de gente “de confianza”. Estas autoridades firman los certificados y dan fe de que el certificado original es de quien dice ser, y si la firma no está bien, se detectarán los certificados como no válidos. Nos dejará hacer la conexión segura si queremos, pero podremos saber que “ahí pasa algo”.

Las autoridades certificadoras que podemos utilizar cuestan dinero, al final son empresas que miran tus datos y hacen de “notario” con esa información (y los notarios son caros). Dependiendo del empeño que pongan en verificar tu identidad son más o menos caros (entre otras cosas). También podemos crearnos nosotros un certificado y firmarnos con él, pero como no somos una autoridad de confianza, el navegador se quejará igual. Aunque si confiamos en nosotros mismos, podemos instalar nuestro certificado en nuestro ordenador y decirle que somos de confianza, pero si ahora llamo a un amigo para que se meta en mi web segura, para él, no somos de confianza.

Bueno, para terminar con esta parte, decir que es una intro super rápida en tres párrafos, falta mucho por decir y hay un montón de cosas entre medias y que, la teoría es muy bonita, pero yo siempre digo una cosa: “Si un dato se puede interceptar, échate a temblar”, o lo que es lo mismo, si alguien puede tener tu información, aunque sea codificada, tarde o temprano, con ganas e ilusión, podrá descifrarla. De hecho muy a menudo se descubren formas de descifrar estas conexiones, extraer las claves, etc.

A lo que vamos

Pero bueno, queremos hacer más o menos eso, en C, como los valientes, aunque utilizando la biblioteca OpenSSL (seré valiente, pero implementar todas las tecnologías necesarias desde cero es demasiado). Ahora, desde OpenSSL, me gustaría conocer si la validación del certificado es buena (pueden fallar mil cosas si miramos la documentación), de todas formas, nuestro cliente es un valiente y termina lo que ha venido a hacer sin que le importe nuestro vecino de las WiFis.

Queremos también información del certificado, que nos diga con quién estamos conectando, la validez del certificado (fecha de emisión y de caducidad o NotBefore y NotAfter) y autoridad certificadora que lo emite (puede que en lugar de una autoridad, haya una cadena de firmas: A (autoridad gorda) firma a B (autoridad intermedia) que firma a C (el servidor), pero como A deposita su confianza en B y B la deposita en C, podemos decir que C es de confianza. Aunque también sabemos cuántos niveles hay y podemos establecer que pasados dos niveles ya no confiamos (en ese caso, si hubiera un D, diríamos que no confiamos en él).

Para eso y mucho más he hecho este ejemplo:

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
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
/**
*************************************************************
* @file myssl.c
* @brief SSL Client HTTP Connection example getting information
*     and verifying certificates.
* These are just some SSL notes
*
* @author Gaspar Fernández <blakeyed@totaki.com>
* @version 0.1
* @date 17 abr 2015
*
* To compile:
*  $ gcc -o myssl myssl.c -lcrypto -lssl
*
*************************************************************/


#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/select.h>
#include <sys/time.h>

/** Where to look for the Certificate Authorities */
#define CAPATH "/etc/ssl/certs"
/** 16Kb SSL_read block size */
#define BUFFERSIZE 16384
/** For temporary strings */
#define STRBUFFERSIZE 256
/** User agent string  */
#define USERAGENT "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0"
/** CRLF  */
#define CRLF "\r\n"

/* Useful Structure. Just to have everything in one place and avoid
 passing lot of arguments to the functions. */


/** sslc handles SSL and SSL_CTX and socket */
typedef struct
{
  /** socket handler  */
  int skt;
  /** error, if any  */
  int err;
  /** SSL handler  */
  SSL* ssl;
  /** SSL Context  */
  SSL_CTX* ctx;
} Sslc;

/* First some useful stuff */

/**
 * Transforms ASN1 time sring to time_t (except milliseconds and time zone)
 * Ideas from: http://stackoverflow.com/questions/10975542/asn1-time-conversion
 *
 * @param time    SSL ASN1_TIME pointer
 * @param tmt     time_t pointer to write to
 *
 * @return int 0 if OK, <0 if anything goes wrong
 */

int ASN1_TIME_to_time_t(ASN1_TIME* time, time_t *tmt);

/**
 * Extract a substring from origin into buffer, updating starting
 * value to call in chain. Used by ASN1_TIME_to_time_t to extract
 * substrings easyly
 *
 * @param buffer  Where to write to
 * @param origin  Original string
 * @param from    Where to start from.
 *                           Updated to the last position after end.
 * @param size    Characters to extract.
 *
 * @return char* reference to buffer
 */

char* join(char* buffer, const char* origin, size_t *from, size_t size);

/**
 * Check certificate validity
 *
 * @param       certificate Certificate to check
 *
 * @return      error code (0 is OK. See x509_vfy.h for
 *              constants (X509_V_ERR_UNABLE_*). You can also
 *              use X509_verify_cert_error_string(long int) to
 *              see the error string
 */

long int check_certificate_validity(X509* certificate);

/**
 * Creates a basic TCP client connection to a server on a port.
 * Uses simple sockets
 *
 * @param h       Our structure. Only the socket will be used
 * @param server  Where to connect
 * @param port    The port to use (443 for HTTPS)
 *
 * @return int       (0 if OK, else fail)
 */

int TCP_Connection(Sslc* h, char* server, int port);

/**
 * Uses select to test if there is anything waiting to be read.
 *
 * @param h       Our structure. Only the socket will be used
 * @param timeout Timeout before giving up
 *
 * @return (0 timeout, 1 data waiting, <0 fail)
 */

int TCP_select(Sslc* h, double timeout);

/**
 * SSL initialization and handshake
 *
 * @param h       Our structure.
 *
 * @return (0 if OK, else fail)
 */

int SSL_Connection(Sslc* h);

/**
 * SSL send. To be called instead of send. It will send data through
 * the socket and decode information, or even perform a handshake
 * if needed.
 *
 * @param h       Our structure.
 * @param msg     Message to send

 * @return (0 if OK, else fail)
 */

int SSL_send(Sslc* h, char* msg);

/**
 * SSL recv. To be called instead of recs. It will read the socket
 * and decode information, or even perform a handshake if needed
 *
 * @param h       Our structure.
 * @param data    Data to be read (caution a pointer by
 *                           reference that must be freed manually

 * @return (0 if OK, else fail)
 */

int SSL_recv(Sslc* h, char** data);

/**
 * Prints out SSL information: SSL Version, cipher used and certificate
 * information.
 *
 * @param h       Our structure.
 *
 * @return void
 */

void SSL_print_info(Sslc* h);

/**
 * Prints out certificate information. Run throught the entries, print the
 * not before and not after information and verify the certificate.
 *
 * @param cert       The certificate to check
 *
 * @return void
 */

void SSL_print_certificate_info(X509* cert);

/**
 * Gets cipher description in a string
 * Please free the resulting string, don't do it like me ;)
 *
 * @param cipher       Cipher
 *
 * @return String with the description
 */

char *SSL_cipher_description(SSL_CIPHER* cipher);

/**
 * Gets a string with the time_t into a string
 *
 * @param buffer      Buffer to write to
 * @param bufsize     Total buffer size
 * @param format      Date/Time format (@see strftime())
 * @param tim         Time
 *
 * @return buffer
 */

char *time_t_to_str(char *buffer, size_t bufsize, const char* format, time_t *tim);

/**
 * Prints ASN1_TIME on screen
 *
 * @param asn1time     Time to write
 * @param pre_string   String to write before the date
 * @param dateformat   Date format (@see strftime())
 *
 * @return void
 */

void print_time(ASN1_TIME* asn1time, char* pre_string, char* dateformat);


/**
 * Prints program usage
 *
 * @param executable   Program executable (argv[0])
 *
 * @return void
 */

void print_usage(char* executable);

/**
 * Prints a tragic error and exit
 *
 * @param msg   Error text
 *
 * @return void
 */

void panic(char *msg);

int main(int argc, char *argv[])
{
  Sslc sslc;
  char *response;
  char *server = argv[1];
  int port;
  char *httpquerytemplate ="GET / HTTP/1.1" CRLF "Host: %s" CRLF "User-Agent: %s" CRLF CRLF;
  char httpquery[1024];

  /* What will be sent to the server */
  sprintf (httpquery, httpquerytemplate, server, USERAGENT);

  if (argc<2)
    print_usage(argv[0]);

  if (argc>2)
    port = atoi (argv[2]);
  else
    port = 443;         /* default https port */

  if (TCP_Connection(&sslc, server, port)<0)
    panic ("Couldn't connect host");

  if (SSL_Connection(&sslc)<0)
    panic ("Couldn't stablish secure connection");

  if (SSL_send(&sslc, httpquery)<0)
    panic ("Couldn't send anything to the server");

  SSL_print_info(&sslc);

  if (SSL_recv(&sslc, &response)<0)
    panic ("Couldn't receive the message");

  printf ("Received %lu bytes\n", strlen(response));

  free(response);

  return EXIT_SUCCESS;
}

char* join(char* buffer, const char* origin, size_t *from, size_t size)
{
  size_t i=0;
  while (i<size)
    {
      buffer[i++] = origin[(*from)++];
    }
  buffer[i] = '\0';
  return buffer;
}

int ASN1_TIME_to_time_t(ASN1_TIME* time, time_t *tmt)
{
  const char* data = time->data;
  size_t p = 0;
  char buf[5];
  struct tm t;
  int temp;
  memset(&t, 0, sizeof(t));
  size_t datalen = strlen(data);

  if (time->type == V_ASN1_UTCTIME) {/* two digit year */
    /* error checking YYMMDDHH at least */
    if (datalen<8)
      return -1;
    t.tm_year = atoi (join(buf, data, &p, 2));
    if (t.tm_year<70)
      t.tm_year += 100;
    datalen = strlen(data+2);
  } else if (time->type == V_ASN1_GENERALIZEDTIME) {/* four digit year */
    /* error checking YYYYMMDDHH at least*/
    if (datalen<10)
      return -1;

    t.tm_year = atoi (join(buf, data, &p, 4));
    t.tm_year -= 1900;
    datalen = strlen(data+4);
  }

  /* the year is out of datalen. Now datalen is fixed */

  t.tm_mon = atoi (join(buf, data, &p, 2))-1; /* January is 0 for time_t */
  t.tm_mday= atoi (join(buf, data, &p, 2));
  t.tm_hour= atoi (join(buf, data, &p, 2));

  if (datalen<8)
    return !(*tmt = mktime(&t));
  t.tm_min = atoi (join(buf, data, &p, 2));

  if (datalen<10)
    return !(*tmt = mktime(&t));
  t.tm_sec = atoi (join(buf, data, &p, 2));

  /* Ignore millisecnds and time zone */
  return !(*tmt = mktime(&t));
}

long int check_certificate_validity(X509* certificate)
{
  int status;
  X509_STORE_CTX *ctx;
  ctx = X509_STORE_CTX_new();
  X509_STORE *store = X509_STORE_new();
  X509_STORE_load_locations(store, NULL, CAPATH);
  X509_STORE_add_cert(store, certificate);

  X509_STORE_CTX_init(ctx, store, certificate, NULL);

  status = X509_verify_cert(ctx);

  return ctx->error;
}

int TCP_Connection(Sslc* h, char* server, int port)
{
  h->err = 0;
  struct hostent *host = gethostbyname (server);
  struct sockaddr_in addr;

  if (host == NULL)
    return -1;          /* Couldn't get host */

  h->skt = socket (AF_INET, SOCK_STREAM, 0);
  if (h->skt < 0)
    {
      return -2;
    }
  else
    {
      /* fill in address data */
      addr.sin_family = AF_INET;
      addr.sin_port = htons (port);
      addr.sin_addr = *((struct in_addr *) host->h_addr);
      bzero (&(addr.sin_zero), 8);

      /* connect */
      h->err = connect (h->skt, (struct sockaddr *) &addr, sizeof (struct sockaddr));
      if (h->err == -1)
        {
      return -3;
        }
    }
  return 0;
}

int SSL_Connection(Sslc* h)
{
  SSL_library_init();       /* not reentrant! */
  SSL_load_error_strings();

  /* try SSL methods (TLSv1.2 ... SSLv3 and SSLv2 (caution! SSLv2 and SSLv3 are deprecated!) */
  /* you could use TLSv1_client_method(), TLSv1_2_client_method()... */
  h->ctx = SSL_CTX_new(SSLv23_client_method());
  if (h->ctx == NULL)
    return -3;          /* Context not created */

  /* SSL will fail if cerificate can't be validated */
  /* h->ctx->verify_mode = 1; */

  h->ssl = SSL_new (h->ctx);
  if (h->ssl == NULL)
    return -4;          /* SSL struct not created */

  if (SSL_set_fd(h->ssl, h->skt) == 0)
    return -5;          /* Couldn't bind SSL with our connection */

  if (SSL_CTX_load_verify_locations(h->ctx, NULL, CAPATH) == 0)
    return -6;          /* Couldn't load verify locations */

  /* Verify depth. How many certs from the chain to verify */
  /* -1 to verify all certificates. */
  printf ("VDepth: %d\n", SSL_get_verify_depth(h->ssl));

  if (SSL_connect (h->ssl) < 1)
    return -7;          /* Couldn't finish SSL handshake */

  /* Get certificate information */
  return 0;
}

int SSL_send(Sslc* h, char* msg)
{
  int bytes = SSL_write(h->ssl, msg, strlen(msg));
  if (bytes<1)
    h->err = bytes;

  return bytes;
}

int TCP_select(Sslc* h, double timeout)
{
  fd_set fds;
  FD_ZERO(&fds);
  FD_SET(h->skt, &fds);
  fd_set *rset=&fds;
  fd_set *wset=NULL;

  struct timeval tv;
  tv.tv_sec = (int)(timeout);
  tv.tv_usec = (int)((timeout - (int)(timeout)) * 1000000.0);

  int ret = select(h->skt+1, rset, wset, NULL, &tv);
  return ret;
}

int SSL_recv(Sslc* h, char** data)
{
  size_t bytes, totalbytes = 0;
  char buffer[BUFFERSIZE];
  int sel;
  size_t allocated = BUFFERSIZE;
  *data = malloc (sizeof(char) * BUFFERSIZE);
  *data[0] = '\0';

  while (1)
    {
      sel = TCP_select(h, 1);
      if (sel<0)
    return -6;      /* select fail */
      else if (sel==0)
    {
      return totalbytes;
    }
      /* We must include terminator after reading */
      bytes = SSL_read (h->ssl, buffer, BUFFERSIZE -1);
      buffer[bytes] = '\0';

      if (bytes<1)
    return -7;

      if (totalbytes + bytes > allocated)
    {
      allocated+=BUFFERSIZE;
      *data = realloc(*data, allocated * sizeof(char));
    }
      strcat(*data, buffer);
      totalbytes+=bytes;
    }
  return totalbytes;
}

void SSL_print_info(Sslc* h)
{
  long vresult = SSL_get_verify_result (h->ssl);
  X509* cert;
  STACK_OF(X509) *chain;
  int i;

  printf ("Verify result: %ld (%s)\n", vresult, X509_verify_cert_error_string(vresult));
  printf ("Verify mode: %d\n", SSL_get_verify_mode(h->ssl)); /* If it's 1 SSL will fail if not verified */
  printf ("SSL version: %s\n", SSL_get_version(h->ssl));
  printf ("Cipher name : %s\n", SSL_get_cipher_name(h->ssl));
  printf ("Cipher version: %s\n", SSL_get_cipher_version(h->ssl));
  printf ("Cipher description: %s\n", SSL_cipher_description((SSL_CIPHER*)SSL_get_current_cipher(h->ssl)));
  printf ("--------------------------------------------\n");
  printf ("Certificate:\n");
  cert = SSL_get_peer_certificate(h->ssl);
  if (cert)
    {
      SSL_print_certificate_info(cert);
      /* X509_free(cert); */
    }
  else
    fprintf(stderr, "------ ERROR: Peer certificate not present!!\n");

  printf ("--------------------------------------------\n");
  printf ("The entire certificate chain: \n");
  chain = SSL_get_peer_cert_chain(h->ssl);
  if (chain)
    {
      printf ("Certificates on chain: %d\n", sk_X509_num(chain));
      for (i=0; i<sk_X509_num(chain); i++)
    {
      cert = sk_X509_value(chain, i);
      if (cert)
        {
          SSL_print_certificate_info(cert);
          /* Not a good idea to free, it's an internal pointer and may
             break something*/

          /* X509_free(cert); */
          printf ("            ·················\n");
        }
    }
    }
  else
    fprintf (stderr, "------- ERROR: Couldn't get certificate chain!!\n");
}

void SSL_print_certificate_info(X509* cert)
{
  X509_NAME *certname;
  X509_NAME_ENTRY* entry;
  char *s;
  char buffer[STRBUFFERSIZE];
  int i, n;

  certname = X509_get_subject_name(cert);
  for (i=0; i< X509_NAME_entry_count(certname); i++)
    {
      entry = X509_NAME_get_entry(certname, i);
      if (entry == NULL)    /* error test. May exit the loop */
    continue;

      /* extracted from X509_NAME_print_ex() */
      int n = OBJ_obj2nid(entry->object);
      if ((n == NID_undef) || ((s = (char*)OBJ_nid2sn(n)) == NULL))
    {
      i2t_ASN1_OBJECT(buffer, sizeof(buffer), entry->object);
      s = buffer;
    }
      printf ("%s = %s\n", s, entry->value->data);
      /* We must NOT free entries (they are internal pointers) */
    }
  print_time(X509_get_notBefore(cert), "Not Before", "%d/%m/%Y %H:%M:%S");
  print_time(X509_get_notAfter(cert),  "Not After", "%d/%m/%Y %H:%M:%S");
  printf ("Valid certificate?: %s\n", X509_verify_cert_error_string(check_certificate_validity(cert)));

  /* We must NOT free X509_get_subject_name() objects */
  /* X509_NAME_free(certname); */
}

char *time_t_to_str(char *buffer, size_t bufsize, const char* format, time_t *tim)
{
  struct tm _tm;
  gmtime_r(tim, &_tm);
  strftime(buffer, bufsize, format, &_tm);
  return buffer;
}

void print_time(ASN1_TIME* asn1time, char* pre_string, char* dateformat)
{
  time_t tim;
  char buffer[STRBUFFERSIZE];

  if (ASN1_TIME_to_time_t(asn1time, &tim)==0)
    printf ("%s: %s\n", pre_string, time_t_to_str(buffer, STRBUFFERSIZE, dateformat, &tim));
  else
    printf ("%s: (error)\n", pre_string);
}

char *SSL_cipher_description(SSL_CIPHER* cipher)
{
  char *tmp = malloc(sizeof(char)*STRBUFFERSIZE);
  tmp[0] = '\0';
  if (cipher)
    SSL_CIPHER_description(cipher, tmp, STRBUFFERSIZE);

  return tmp;
}

void print_usage(char *executable)
{
  fprintf(stderr, "You must specify a web server to connect through HTTPS and additionally a port:\n");
  fprintf(stderr, "  %s server [port]\n", executable);
  fprintf(stderr, "------------\n\n");
  exit(-1);
}

void panic(char *msg)
{
  fprintf (stderr, "Error: %s (errno %d, %s)\n", msg, errno, strerror(errno));
  /* Print SSL errors */
  ERR_print_errors_fp(stderr);
  exit(2);
}

Y además, lo he subido a github por lo que se podrá ir actualizando.

Para compilar:

$ gcc -o myssl myssql.c -lcrypto -lssl

y para ejecutar:

$ ./myssl servidor [puerto]

Alguna cosa curiosa en el código

Algo digno de mención en el código anterior es que para hacer lectura y escritura de mensajes ya no usaremos send() ni recv(), lo que usábamos de toda la vida para conectar con sockets, ahora usaremos SSL_read() y SSL_write() que se encargarán entre otras cosas de cifrar/descifrar, enviar/recibir la información y si hay algún problema con las claves o la negociación, intentar solucionarlo. Los sockets no los usaremos más (a no ser que queramos algo específico, y como mucho usarlos para hacer un select() porque los datos que pasan por ellos no tendrán mucho sentido para nosotros. Desde que llamamos a SSL_set_fd() podemos olvidarnos.

Muchas funciones de obtención de información de certificados: sk_X509_value(), X509_NAME_get_entry() devuelven un puntero, y como somos de C, todos los punteros que reservamos, deben ser liberados, sobre todo, si en la documentación encontramos cosas como X509_NAME_free(), pero no debe ser así. Sólo llamaremos a xxx_free() y hemos llamado previamente a xxx_new(), si no, se están devolviendo punteros internos que se están usando para nuestro programa.

No he incluido constantes de error ni nada, es sólo una prueba de concepto que quiero incorporar en una pequeña biblioteca que tengo en desarrollo.

¡A jugar!
Foto: AshtonPal (Flickr CC-by)

The post Conexión segura a un servidor web (HTTPS) en C usando openSSL appeared first on Poesía Binaria.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 197

May 03, 2015 11:11 AM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, con la Build 2015 y las jugosas novedades anunciadas como principales protagonista. Espero que os resulten interesantes :-)

.Net

ASP.NET

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Data access

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Cross-platform

Otros




Publicado en Variable not found

» Leer más, comentarios, etc...

Blog Bitix

Escribir en varios «Writer» a la vez

April 30, 2015 06:00 PM

Java

Hace un tiempo tuve necesidad de generar cierto contenido sobre varios writers, la necesidad en concreto era generar un archivo xml en disco y al mismo tiempo el mismo contenido para un correo electrónico. Para no escribir lo mismo en dos Writer diferentes la solución fue crear un writer y este fuese el que escribiese el contenido que se le enviaba sobre varios writers. En la API de Java no hay una clase específica que haga esto pero es muy sencillo hacer una implementación que lo haga, esto va a ser lo que explicaré en el siguiente artículo.

Para hacer que el contenido de un writer se escriba a varios deberemos extender la clase Writer de esta manera su uso será como la de cualquier otro Writer. Lo especial de la implementación del writer es que su misión será realizar la misma operación que se haga sobre él sobre los writers que en este caso se pasan como parámetros en el constructor en forma de varargs.

<noscript><pre><code>package io.github.picodotdev.writer; import java.io.IOException; import java.io.Writer; class MultipleWriter extends Writer { private Writer[] writers; MultipleWriter(Writer... writers) { this.writers = writers; } public Writer append(final char c) throws IOException { doWriters(new Command() { public void process(Writer writer) throws IOException { writer.append(c); } }); return this; } public Writer append(final CharSequence csq) throws IOException { doWriters(new Command() { public void process(Writer writer) throws IOException { writer.append(csq); } }); return this; } public Writer append(final CharSequence csq, final int start, final int end) throws IOException { doWriters(new Command() { public void process(Writer writer) throws IOException { writer.append(csq, start, end); } }); return this; } public void close() throws IOException { doWriters(new Command() { public void process(Writer writer) throws IOException { writer.close(); } }); } public void flush() throws IOException { doWriters(new Command() { public void process(Writer writer) throws IOException { writer.flush(); } }); } public void write(final char[] cbuf) throws IOException { doWriters(new Command() { public void process(Writer writer) throws IOException { writer.write(cbuf); } }); } public void write(final char[] cbuf, final int off, final int len) throws IOException { doWriters(new Command() { public void process(Writer writer) throws IOException { writer.write(cbuf, off, len); } }); } public void write(final int c) throws IOException { doWriters(new Command() { public void process(Writer writer) throws IOException { writer.write(c); } }); } public void write(final String str) throws IOException { doWriters(new Command() { public void process(Writer writer) throws IOException { writer.write(str); } }); } public void write(final String str, final int off, final int len) throws IOException { doWriters(new Command() { public void process(Writer writer) throws IOException { writer.write(str, off, len); } }); } private void doWriters(Command command) throws IOException { for (Writer w : writers) { command.process(w); } } private interface Command { public void process(Writer writer) throws IOException; } }</code></pre></noscript>

El bucle for sobre cada uno de los Writer está encapsulado en el método doWriters, el objeto Command es que realmente hace la escritura en el writer usando el método write que se llamó sobre la clase MultipleWriter. A falta de las funciones lambda hasta Java 8 se usa el objeto Command y el método doWriters, por contra se crea por cada método writer_ invocado se crea una instancia de la clase Command.

Con closures y las novedades de Java 8 en la API no sería necesario que usaramos una clase Command, el código es más sencillo, breve y más legible.

<noscript><pre><code>package io.github.picodotdev.writer; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; class MultipleWriter extends Writer { private Writer[] writers; MultipleWriter(Writer... writers) { this.writers = writers; } public Writer append(final char c) throws IOException { doWriters(writer -&gt; { writer.append(c); }); return this; } public Writer append(final CharSequence csq) throws IOException { doWriters(writer -&gt; { writer.append(csq); }); return this; } public Writer append(final CharSequence csq, final int start, final int end) throws IOException { doWriters(writer -&gt; { writer.append(csq, start, end); }); return this; } public void close() throws IOException { doWriters(writer -&gt; { writer.close(); }); } public void flush() throws IOException { doWriters(writer -&gt; { writer.flush(); }); } public void write(final char[] cbuf) throws IOException { doWriters(writer -&gt; { writer.write(cbuf); }); } public void write(final char[] cbuf, final int off, final int len) throws IOException { doWriters(writer -&gt; { writer.write(cbuf, off, len); }); } public void write(final int c) throws IOException { doWriters(writer -&gt; { writer.write(c); }); } public void write(final String str) throws IOException { doWriters(writer -&gt; { writer.write(str); }); } public void write(final String str, final int off, final int len) throws IOException { doWriters(writer -&gt; { writer.write(str, off, len); }); } private void doWriters(Command command) throws IOException { for (Writer w : writers) { command.process(w); } } private interface Command { public void process(Writer writer) throws IOException; } }</code></pre></noscript>

Independiente de la implementación con Java 7 o con a Java 8 el uso sería el siguiente:

<noscript><pre><code> ... public static void main(String[] args) throws Exception { Writer w = new MultipleWriter(new OutputStreamWriter(System.out), new OutputStreamWriter(System.out)); w.write(&quot;¡Hola mundo!\n&quot;); w.flush(); w.close(); } ...</code></pre></noscript>

Con Groovy además de las closures no será necesario que declararemos de forma explícita el lanzamiento de las excepciones sin embargo al usarlo perderíamos la ayuda que ofrece el compilador.

Referencia:
Otros artículos sobre Java
Novedades y nuevas características de Java 8

» Leer más, comentarios, etc...

Picando Código

Las universidades hispanoamericanas y españolas que más apoyan el software libre en 2015

April 28, 2015 02:03 PM

El Observatorio Tecnológico PortalProgramas lanzó recientemente su Ranking de Universidades en Software Libre (RuSL); un estudio que se ocupa de medir el compromiso, uso y difusión del software libre de 76 universidades españolas y por primera vez de 68 universidades hispanoamericanas.

El criterio

Se analizan todas las universidades de España y las 5 universidades de cada país de Hispanoamérica más destacadas según el ranking QS, uno de los rankings de universidades más prestigiosos del mundo.

El motivo por el que se seleccionan sólo 5 en cada país es que uno de los puntos fuertes de este ranking es que el análisis de cada universidad se realiza manualmente para ofrecer la máxima fiabilidad, con personal dedicando exclusivamente a ello. Y eso hace inviable analizar las más de mil universidades de toda Latinoamérica. Con el objetivo de que todos, o la mayor parte, de países tengan representación y tener un criterio objetivo de selección de universidades, se decidió basarse en un ranking de universidades de prestigio: QS World University Ranking. Y se analizan las universidades de cada país con mayor puntuación en dicho ranking.

En las primeras ediciones del ranking sólo se analizaba España, y por tanto se analizaban todas las universidades del país.

Resultados Universidades Hispanoamericanas

Las universidades que alcanzaron las primeras posiciones en esta clasificación son, por este orden, la de Costa Rica, la Nacional de la Plata, en Argentina, y la Universidad Francisco Marroquín de Guatemala. El cuarto puesto es para la Universidad de la República (UDELAR) de Uruguay :) . A su vez, la UDELAR se muestra como 100% comprometida con la docencia en Software Libre.

Mejores Universidades Hispanoamericanas Software Libre

Resultados Universidades Españolas

Poco a poco, las universidades españolas van consolidando su compromiso con la difusión y el uso de las tecnologías libres. Este año, las universidades de Granada, Las Palmas de Gran Canaria y La Laguna (Tenerife) vuelven a ocupar los tres primeros puestos en la clasificación, si bien se observa cómo muchas otras universidades han incrementado considerablemente sus puntuaciones e irrumpen con fuerza entre las primeras posiciones del ranking.

Mejores Universidades Espanolas Software Libre

Excelente estudio de la gente de PortalProgramas. Todo el contenido de la investigación es liberado bajo licencias Creative Commons y pueden encontrar los datos recogidos en formatos ODS y XLS en este enlace. Ideal para verificar los datos o crear nuevos proyectos a partir de ellos.

» Leer más, comentarios, etc...

Arragonán

Semana 359

April 28, 2015 12:30 PM

Semana marcada por el día de Aragón, que partía la semana resultando no demasiado productiva. Me hubiera gustado haber adelantado más trabajo pero las semanas así resultan más extrañas, aparte he ido algo despistado con otras cuestiones.

En el videoblog Qué Pasa Có publicaron las entrevistas que me hicieron a mi por Mosica, y otra a Javi Rubio y Héctor Rodríguez por La Agendica. Os dejo aquí la mía:

Buena parte del equipo de organización hicimos retrospectiva del Startup Open Space 2015, y encontramos varios puntos de mejora. Un poco en combinación con esto, surgió por twitter el tema de abrir un slack para tocar el tipo de cuestiones que tratamos en el Open Space, y en ello andamos.

Por el festivo, en SenpaiDevs sólo tuvimos una sesión. La aprovechamos para seguir arreglando algunas cuestiones menores en la aplicación que estamos desarrollando, y luego yo hice una pequeña intro a Ruby on Rails a modo de aproximación inicial al framework.

En cuanto a proyectos:

  • One-stop ya estoy cerca de finiquitarlo, queda rematar una de las grandes funcionalidades y darle una vuelta a una cuestión de integración. Aunque me veo venir que aparecerá alguna cosa, como siempre, pero ya está cerca de estar listo para lanzarlo a producción.
  • Tuvimos una sesión de trabajo sobre Mosica, nos centramos en dejar lista la primera versión de las aplicaciones móviles y en hacer la subida a la App Store de Apple. Ahora estamos a la espera de que nos la aprueben.

Buena semana.

» Leer más, comentarios, etc...

Variable not found

¡VB será soportado en ASPNET 5!

April 28, 2015 07:10 AM

ASP.NET 5Sin duda son buenas noticias para los desarrolladores VB.NET, que habían visto cómo su lenguaje favorito era ignorado durante el desarrollo de la nueva oleada de tecnologías ASP.NET 5, también conocida como “ASP.NET vNext” (aunque queda poco tiempo para que se le pueda seguir llamando así ;D)

Y la verdad es que había un poco de inquietud al respecto porque todos los desarrollos y ejemplos publicados hasta la fecha de la nueva pila de de tecnologías para la web de Microsoft utilizaban exclusivamente C#, no había ni rastro VB. Esta sensación, además, se veía reforzada por la publicación de comentarios y artículos donde el futuro de Visual Basic en este entorno no parecía estar demasiado claro.

Cronología del FUD

En diciembre de 2014, Scott Hunter, Principal Program Manager Lead del equipo ASP.NET en Microsoft, respondía en GitHub a un desarrollador que preguntaba sobre la posibilidad de probar ASP.NET 5 con Visual Basic:
“ASP.NET 5 es sólo C# en este momento, y esto no cambiará antes de la RTM. En el futuro planeamos tener puntos de extensibilidad para que otros lenguajes como VB o F# puedan ser añadidos en forma de support packages o similar”
El pasado mes de Enero, Eilon Lipton, Development manager en el equipo de ASP.NET de Microsoft, respondía a una pregunta directa en Stack Overflow sobre si VB.NET sería soportado o no en ASP.NET 5:
“No hay planes para soportar VB en ASP.NET 5 en términos de compilación, plantillas de proyectos y otras herramientas. Esto se está discutiendo en GitHub el siguiente issue del proyecto ASP.NET 5: https://github.com/aspnet/Home/issues/236
Más recientemente, hace sólo un par de meses, Scott Guthrie, jefazo de todo lo relacionado con la nube, servidores y herramientas de desarrollo (entre otras cosas), publicaba el post “Introducing ASP.NET 5” donde repasaba las principales novedades del nuevo stack, y no hacía ninguna referencia expresa al soporte de VB.NET, lo que incrementó el nerviosismo y provocó un intenso debate en los comentarios del artículo.

VBAlgún día después, Stephen Walter (ex-miembro del equipo de ASP.NET) publicaba en su blog un post sobre los 10 cambios más importantes en ASP.NET 5 y MVC 6,  y dejaba caer la siguiente perla:
“No más Visual Basic. Es el momento de decirle adiós a Visual Basic. ASP.NET 5 sólo soporta C# y Visual Basic queda atrás”
Claro, ante estas perspectivas, era lógica la inquietud de los desarrolladores con extensas bases de código en Visual Basic, cuyas aplicaciones MVC quedaban condenadas a no evolucionar más allá de las versiones 5.x del framework…

… Y llegó la respuesta oficial :)

La respuesta oficial ha sido tan sólo hace unos días, cuando Jeff Fritz publicó en el blog del equipo de desarrollo y herramientas .NET para la web un post titulado “Making It Better: ASP.NET with Visual Basic 14” en el que anuncia:
“Hemos hablado de que ASP.NET 5 es una gran actualización del framework ASP.NET, con Roslyn y el soporte cross-platform en mente desde nuestras discusiones públicas iniciales. No es una tarea sencilla, y nos enfocamos en un principio en completar el soporte para C#. Durante los meses transcurridos desde los anuncios iniciales, hemos oído a muchos de vosotros contarnos cuánto os gusta Visual Basic y que queríais verlo soportado en ASP.NET 5.

Hoy nos complace anunciar que ASP.NET 5 tendrá soporte completo para Visual Basic (tanto en herramientas como en tiempo de ejecución, incluidos los entornos cross-platform). Como siempre, continuaremos el desarrollo abierto de ASP.NET 5, y podéis seguir nuestros progresos, o incluso contribuir, en GitHub a través de la dirección  http://github.com/aspnet/home
Con eso, poco lugar queda para la duda. Pero por si acaso, ahí va el tweet de Scott Guthrie apostillándolo poco después:

VB will be fully supported with ASP.NET 5 and .NET Core

Aunque no hay detalles de fechas ni plazos, ahora sí podemos decirlo: programadores de Visual Basic .NET, ¡bienvenidos a ASP.NET 5!

Publicado en Variable not found.

» Leer más, comentarios, etc...

Poesía Binaria

Usando SQLite en nuestros programas en C/C++ (II): Nueva interfaz v2 y prepared statements

April 27, 2015 08:37 AM

Hace unos días empezamos a programar utilizando SQLite para gestionar nuestra información usando SQL de toda la vida.

Bien, SQLite tiene la interfaz clásica, que vimos en el post anterior: sqlite3_open(), sqlite3_close(), sqlite3_exec()… con utilidades básicas para trabajar.

Por un lado, podemos tener más control sobre la base de datos, por ejemplo, podemos definir cómo abirmos la base de datos, tenemos códigos de error extendidos, ventajas con respecto al tratamiento interno de la memoria y algunas cosas más. La utilización es muy parecida.

Prepared statements con SQLite

Por otro lado, el enfoque con callbacks está muy bien, pero en ocasiones necesitamos el resultado de un sqlite3_exec() inmediatamente, justo debajo de esa línea, sin que tengamos que pasar contextos a una función y cambiar nuestra manera de pensar, es decir, hago una query y debajo tengo el resultado. Vamos a utilizar prepared statements con SQLite:

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
#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

int panic(char *error, int code)
{
  fprintf(stderr, "Error (%d): %s\n", code, error);
  exit(-1);
}

int main(int argc, char* argv[])
{
   sqlite3 *db;
   sqlite3_stmt *query;
   char *error = 0;
   int res;
   char *sql;
   int i;

   if ( (res=sqlite3_initialize()) != SQLITE_OK)
     panic("No se puede inicializar SQLite", res);

/* Abre la base de datos como sólo lectura, ver constantes SQLITE_OPEN_* para más info. */
   if ( (res=sqlite3_open_v2("test.db",  &db, SQLITE_OPEN_READONLY, NULL) )!= SQLITE_OK)
     panic("No puedo abrir la base de datos", res);

/* Prepara la sonsulta SQL */
   if ( (res=sqlite3_prepare_v2(db, "SELECT * FROM events", -1, &query, NULL) )!= SQLITE_OK)
     panic("No puedo ejecutar la consulta", res);

   while ( (res = sqlite3_step(query)) == SQLITE_ROW)
     {
       printf ("Filas encontradas: %d\n", sqlite3_data_count(query));
       for (i=0; i< sqlite3_column_count(query); ++i)
     {
       printf ("%s (%s) (Type: %d) => (%d bytes) %s\n",
           sqlite3_column_name(query, i),
           sqlite3_column_decltype(query, i),
           sqlite3_column_type(query, i),
           sqlite3_column_bytes(query, i),
           sqlite3_column_text(query, i));
     }
       printf("\n");
     }
   /* Finaliza la consulta y realiza limpieza */
   sqlite3_finalize(query);

   sqlite3_close_v2(db);

   /* Open database */
   return 0;
}

Aquí vemos cómo podemos lanzar la SELECT y obtener debajo el resultado, con alguna información adicional (para conocer algunas funciones más de SQLite), para ello tendremos:

  • sqlite3_step(sqlite3_stmt*) : Itera entre los resultados devueltos en la consulta. Si tenemos una consulta que devuelve 4 resultados, tendremos que llamar a esta función 4 veces, cada vez podrá extraer los datos de una fila. Si todo va bien devuelve SQLITE_ROW, si no hay más filas, SQLITE_DONE y otra cosa en caso de error.
  • sqlite3_data_count(sqlite3_stmt*) : Nos devuelve el número de filas. Siempre y cuando hayamos hecho un sqlite3_step() antes.
  • sqlite3_column_count(sqlite3_stmt*) : Devuelve el número de columnas que hay en la fila actual
  • sqlite3_column_name(sqlite3_stmt*, int index) : Devuelve el nombre de la columna número index
  • sqlite3_column_decltype(sqlite3_stmt*, int) : Devuelve el tipo de dato exacto de la columna especificada. Aunque en SQLite tenemos sólo 5 tipos de dato (NULL, INT, FLOAT, BLOB, TEXT), hay datos derivados mucho más complejos, por ejemplo DATETIME para fecha y hora, que convierte directamente a TEXT.
  • sqlite3_column_type(sqlite3_stmt*, int) : Devuelve el tipo de dato de SQLite para la columna dada, (SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB, SQLITE_NULL)
  • sqlite3_column_bytes(sqlite3_stmt*, int) : Devuelve la longitud en bytes del dato de la columna (como si fuera un string)
  • sqlite3_column_text(sqlite3_stmt*, int) : Devuelve el dato de la columna especificada en forma de cadena de caracteres (char*))

Escogiendo el tipo de variable correcto en C

Anteriormente hemos visto cómo los valores devueltos por SQLite se convierten a string (todos son convertibles), y podemos verlos en pantalla. Aunque a veces, seguro que no nos interesa eso, si pedimos un número a SQLite, lo queremos en forma de número, sin necesidad de transformarlo luego. Con este segundo ejemplo, vemos que dependiendo del valor devuelto por sqlite3_column_type() extraemos el valor con uno u otro tipo:

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
#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

int panic(char *error, int code)
{
  fprintf(stderr, "Error (%d): %s\n", code, error);
  exit(-1);
}

int main(int argc, char* argv[])
{
   sqlite3 *db;
   sqlite3_stmt *query;
   char *error = 0;
   int res;
   char *sql;
   int i;

   if ( (res=sqlite3_initialize()) != SQLITE_OK)
     panic("No se puede inicializar SQLite", res);

   if ( (res=sqlite3_open_v2("test.db",  &db, SQLITE_OPEN_READONLY, NULL) )!= SQLITE_OK)
     panic("No puedo abrir la base de datos", res);

   if ( (res=sqlite3_prepare_v2(db, "SELECT * FROM events", -1, &query, NULL) )!= SQLITE_OK)
     panic("No puedo ejecutar la consulta", res);

   while ( (res = sqlite3_step(query)) == SQLITE_ROW)
     {
       printf ("Filas encontradas: %d\n", sqlite3_data_count(query));
       for (i=0; i< sqlite3_column_count(query); ++i)
     {
       printf ("%s (%s) (Type: %d) => (%d bytes)",
           sqlite3_column_name(query, i),
           sqlite3_column_decltype(query, i),
           sqlite3_column_type(query, i),
           sqlite3_column_bytes(query, i));
       switch (sqlite3_column_type(query, i))
         {
         case SQLITE_INTEGER:
           printf ("%d", sqlite3_column_int(query,i));
           break;
         case SQLITE_FLOAT:
           printf ("%lf", sqlite3_column_double(query,i));
           break;
         case SQLITE_BLOB:
           printf ("BINARIO");
           break;
         case SQLITE_TEXT:
           printf ("%s", sqlite3_column_text(query,i));
           break;
         case SQLITE_NULL:
           printf ("NULL");
           break;
         default:
           printf ("Not supported"); /* No deberíamos entrar nunca */
         }
       printf("\n");
     }
       printf("\n");
     }
   /* query cleanup */
   sqlite3_finalize(query);

   sqlite3_close_v2(db);

   /* Open database */
   return 0;
}

Vinculando argumentos (binding arguments)

Además de la precompilación de las consultas que vamos a utilizar para poder ejecutarlas más rápido (sobre todo si ejecutamos la consulta repetidas veces), una de las ventajas de los prepared statements es que nos permiten vincular argumentos de forma segura en una consulta. Siempre hay caracteres que no se pueden pasar en un sistema, cuando los argumentos son de tipo TEXT, si metemos una comilla entre el texto podemos cerrar la query y puede que no queramos eso. También puede ser utilizado por usuarios malintencionados para romper nuestros programas. Y bueno, también nos vale para tratar todos los argumentos que pasamos a SQLite de la misma forma, y olvidarnos de poner comillas en los textos, dejar los números sin ellas y poner bien los NULL, vamos a hacer una pequeña consulta con parámetros sobre el mismo programa de antes:

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
#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

int panic(char *error, int code)
{
  fprintf(stderr, "Error (%d): %s\n", code, error);
  exit(-1);
}

int main(int argc, char* argv[])
{
   sqlite3 *db;
   sqlite3_stmt *query;
   char *error = 0;
   int res;
   char *sql;
   int i;

   if ( (res=sqlite3_initialize()) != SQLITE_OK)
     panic("No se puede inicializar SQLite", res);

   if ( (res=sqlite3_open_v2("test.db",  &db, SQLITE_OPEN_READONLY, NULL) )!= SQLITE_OK)
     panic("No puedo abrir la base de datos", res);

   if ( (res=sqlite3_prepare_v2(db, "SELECT * FROM events WHERE timestamp>? AND timestamp<? AND level>? AND message LIKE ?", -1, &query, NULL) )!= SQLITE_OK)
     panic("No puedo ejecutar la consulta", res);

   printf("Tengo que vincular %d parámetros\n", sqlite3_bind_parameter_count(query));

   if ( (res=sqlite3_bind_text(query, 1, "2015-01-01", -1, SQLITE_STATIC)) )
     panic("No puedo vincular el argumento 1", res);

   if ( (res=sqlite3_bind_text(query, 2, "2015-04-01", -1, SQLITE_STATIC)) )
     panic("No puedo vincular el argumento 2", res);

   if ( (res=sqlite3_bind_int(query, 3, 12) ) )
     panic("No puedo vincular el argumento 3", res);

   if ( (res=sqlite3_bind_text(query, 4, "%again%", -1, SQLITE_STATIC)) )
     panic("No puedo vincular el argumento 4", res);

   while ( (res = sqlite3_step(query)) == SQLITE_ROW)
     {
       printf ("Filas encontradas: %d\n", sqlite3_data_count(query));
       for (i=0; i< sqlite3_column_count(query); ++i)
     {
       printf ("%s (%s) (Type: %d) => (%d bytes)",
           sqlite3_column_name(query, i),
           sqlite3_column_decltype(query, i),
           sqlite3_column_type(query, i),
           sqlite3_column_bytes(query, i));
       switch (sqlite3_column_type(query, i))
         {
         case SQLITE_INTEGER:
           printf ("%d", sqlite3_column_int(query,i));
           break;
         case SQLITE_FLOAT:
           printf ("%lf", sqlite3_column_double(query,i));
           break;
         case SQLITE_BLOB:
           printf ("BINARIO");
           break;
         case SQLITE_TEXT:
           printf ("%s", sqlite3_column_text(query,i));
           break;
         case SQLITE_NULL:
           printf ("NULL");
           break;
         default:
           printf ("Not supported"); /* No deberíamos entrar nunca */
         }
       printf("\n");
     }
       printf("\n");
     }
   /* query cleanup */
   sqlite3_finalize(query);

   sqlite3_close_v2(db);

   /* Open database */
   return 0;
}

En este caso, vemos que hemos colocado interrogaciones en la query inicial, cada interrogación será sustituída por un valor que luego especificaremos con sqlite3_bind_xxxx(), en este caso, vamos a especificar rangos de fechas y un número.

sqlite3_bind_int() sólo necesita la query, la posición del parámetro que va a modificar (¡¡ojo!! El primero es el 1 y no el 0) y su valor.

sqlite3_bind_text() es un poco especial, son cadenas de caracteres, y sabemos que C es muy suyo para eso. Tenemos que pasarle:

  • La query, vamos el puntero al sqlite_stmt para el que queremos asignar el parámetro
  • El número de parámetro a vincular
  • La cadena que queremos vincular… hasta aquí vamos bien, es similar al sqlite_bind_int()
  • Tamaño de la cadena (por si tenemos un número exacto de caracteres), pero si no es así, ponemos un número negativo y cogerá hasta el primer terminador (\0) que encuentre.
  • SQLITE_STATIC o SQLITE_TRANSIENT, dependiendo de si el valor de la cadena que hemos pasado cambiará con el tiempo o no. Como el valor tenemos que conservarlo, imaginemos que usamos una variable para especificar la cadena, y dicha variable será sobreescrita en breve (puede que porque sea una variable temporal, o porque los sqlite3_bind_text() están dentro de una función y las cadenas son variables locales). Como aquí vemos que la cadena no cambia usamos SQLITE_STATIC, pero en caso de duda, mejor usamos SQLITE_TRANSIENT, hacemos que SQLite se haga una copia de la cadena, y nos curamos en salud.

SQLite y C++

Es cierto que todos los ejemplos que he puesto son para lenguaje C, sin ++, aunque podemos utilizarlo de forma muy parecida. De todas formas, seguro que los programadores de C++ agradecen tener una biblioteca que utilice esta base de datos a la manera C++ (the C++ way!), y que ahorre un poco de tiempo, y escritura.
Hay varias bibliotecas para utilizarlo, pero la única que he visto más o menos en desarrollo todavía es sqlite3pp (Al menos, actualizada en 2015).
En su github hay varios ejemplos interesantes.
Foto: Travis Warren (Flickr CC-by)

The post Usando SQLite en nuestros programas en C/C++ (II): Nueva interfaz v2 y prepared statements appeared first on Poesía Binaria.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 196

April 27, 2015 07:15 AM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, espero que os resulten interesantes :-)

.Net

ASP.NET

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Otros

Publicado en Variable not found

» Leer más, comentarios, etc...

Koalite

Principio de Segregación de Interfaces para Abuelas

April 27, 2015 05:06 AM

Hace un par de semanas escribía sobre lo que realmente significa el principio de inversión de independencias con el objetivo de intentar profundizar más allá de la implementación típica y poder ver en qué ideas se basa y dónde más podemos aplicar esas ideas.

Siguiendo con esa línea, en este post vamos a aprovechar para analizar el trasfondo del principio de segregación de interfaces (PDF), la I de SOLID, que seguramente sea uno de los principios de SOLID menos citados y menos tenidos en cuenta de los cinco.

abuela-preocupada

Segregación de interfaces para abuelas

Imagina que tu abuela quiere contratar una línea de teléfono fijo para hablar con sus amigas. Cuando va a hablar con la operadora de turno, encuentra una oferta en la que, por el mismo precio que una línea, le ofrecen un pack que incluye la línea de teléfono, internet y televisión por cable.

Aunque tu abuela sólo va a usar la línea, ya que le cuesta lo mismo una cosa que otra, decide contratar el pack. Total, así si un día quiere aprender eso del internet o ver algún programa en un canal de los raros, puede hacerlo.

Unas semanas más tarde, un día descubre que no puede llamar por teléfono a su amiga Puri para felicitarle por su cumpleaños. Parece ser que la línea de teléfono de su pack utiliza algo que se llama voz sobre ipé o algo así, y cuando no funciona el aparato rúter, no se puede llamar. Por suerte un simpático técnico de la operadora le cambia el rúter y le soluciona el problema, pero ya no consigue felicitar a tiempo a Puri en su cumpleaños (y a ciertas edades no sé sabe cuántas oportunidades quedan para ello).

Otro día, tampoco puede llamar a Dora para acercarse a visitarla. Una chica muy agradable de la operadora le explica que es que tienen que actualizar remotamente el finguar de su rúter para darle más megas del internet, y que mientras lo hacen no podrá conectarse y su voz sobre ipé no funcionará.

Estos problemas que ha tenido tu (hipotética) abuela, vienen motivados por violar el principio de segregación de interfaces.

Tu abuela sólo quería hablar por teléfono, pero ha adquirido una dependencia sobre algo mucho mayor, el pack internet+llamadas+televisión, por lo que cuando algo de ese pack falla, aunque sea algo que ella no utiliza para nada, se ve afectada por ello.

El enunciado formal

En el artículo que enlazaba antes, Robert C. Martin explica el principio de segregación de interfaces en detalle, pero no sé si es porque el artículo ha resistido mal el paso del tiempo o ese día el autor estaba poco inspirado, pero la argumentación resulta a veces un poco confusa y, el diseño final al que llega es, al menos desde mi punto de vista, bastante discutible.

Sin embargo, la idea de base es sencilla y bastante razonable: si tienes un componente “grande” que es consumido por varios clientes, y cada cliente utiliza sólo ciertas características del componente “grande”, ese cliente debería depender sólo de esas características. El enunciado formal es:

Los clientes no deben verse obligados a depender de interfaces que no utilizan.

Que traducido a lenguaje de abuelas sería:

No contrates cosas que no necesitas.

Es importante tener en cuenta que cuando hablamos de interfaces, no estamos (necesariamente) hablando de interfaces como los de Java o C#, sino del API que expone un objeto concreto. Tal vez un término más apropiado sería contratos, que es el que usaré en el resto de post para distinguir entre estos interface/APIs, y los interfaces de C#/Java.

El trasfondo de todo esto

¿Qué es lo malo de tener contratos grandes? A fin de cuentas, puede ser que estemos agrupando funcionalidad que conceptualmente esté relacionada y, a priori, tendría sentido mantenerla unida en aras de aumentar la cohesión. Además, puestos a incluir a una dependencia, mejor tener una que hace más cosas por si acaso en algún momento me hacen falta, ¿no?

El problema, como casi siempre, es el acoplamiento. La típica disyuntiva entre cohesión y acoplamiento que se produce al desarrollar software.

Si tenemos un contrato grande pero cada cliente utiliza una parte diferente, estamos acoplando implícitamente unos clientes con otros a través de ese contrato.

En el ejemplo de la abuela, el mismo contrato (el pack completo) sirve a clientes que sólo quieren llamar, como ella, y a otros que quieren navegar por internet. Para mejorar el servicio de los clientes que usan internet (darles más megas) hace falta cambiar la implementación (actualizar el finguar del rúter) y eso implica pérdida de servicio para clientes que, en principio, no tendrían motivos para verse afectados por ello.

Si lo llevamos a código, tendríamos algo como esto:

public interface IMegaPack {
    void MakePhoneCall();
    void ConnectToInternet();
    void WatchCableTV();
}

Es un único interfaz que expone toda la funcionalidad, por lo que obligamos a todos los clientes depender del interfaz completo. La solución es sencilla, podemos partir el interfaz en trozos más pequeños:

public interface IVoice
{
    void MakePhoneCall()
}

public interface IInternet
{
    void ConnectToInternet();
}

public interface ICableTV
{
    void WatchCableTV();
}

public interface IMegaPack : IVoice, IInternet, ICableTV
{
}

Es importante tener en cuenta que partir el interfaz no quiere decir que partamos su implementación. Técnicamente podríamos mantener un único objeto que implementa todos los interfaces y cada cliente lo vería sólo como la parte que le interesa.

Cuando se aplica esta idea de un único objeto que implementa varios interfaces específicos para cada caso de uso, a los interfaces se les suele llamar role interface.

Aunque la situación mejora porque los clientes no quedan acoplados a través del contrato, sí que siguen acoplados a través de la implementación (al final es el mismo objeto), por lo que la solución no es todo lo buena que a uno le gustaría. Dependiendo del caso, esto será más o menos problemático y solucionarlo será más o menos fácil (a veces existe un acoplamiento real entre las funcionalidades y no es sencillo evitarlo).

Otro problema adicional de depender de objetos grandes, es que cuanto mayor es el tamaño de un objeto más probable es que tenga más dependencias, por lo que los clientes estarán asumiendo transitivamente un número mayor de dependencias.

Al crear interfaces más pequeños, tenemos la ventaja adicional de que, si es necesario crear nuevas implementaciones del interfaz para un caso de uso concreto, no necesitaremos implementar también el resto de métodos que no están relacionados con ese caso de uso.

Llevando al extremo la segregación de interfaces, acabaríamos con intefaces degenerados que tienen un único método. Es razonable. En muchos lenguajes a esas cosas se les llaman funciones y a veces son la mejor solución, como decía John Carmack:

A veces, la implementación elegante es sólo una función. No un método. No una clase. No un framework. Sólo una función.

De contratos a conceptos

Si ampliamos un poco nuestros horizontes y dejamos de pensar en contratos como interfaces u objetos, veremos que hay ideas del principio de segregación se puede aplicar en más ámbitos.

Por ejemplo, es fácil abusar de un gestor de paquetes para añadir dependencias sobre un paquete que hace 1000 cosas cuando sólo necesitas 2.

Eso hará que tengamos que asumir como propias dependencias de ese paquete que, en realidad, no están relacionadas con las cosas que utilizamos.

También puede ocurrir con cosas que cuesta considerar interfaces/contratos.

Si tenemos un método que necesita obtener las ventas de un producto, ¿es mejor pasarle el objecto Product o sólo el id? Ya hablamos de eso hace tiempo pero si le pasas el objeto Product y sólo necesita el id, se introducirá una dependencia sobre un contrato mayor (toda la clase Product) por lo que quedará acoplado al resto de clientes de esa clase.

Aplicando el principio de segregación de interfaces, podríamos pensar que si ese método sólo necesita poder obtener un id, no debería quedar acoplado al contrato completo de Product, con su nombre, su precio y unas cuantas decenas de propiedades más.

Muchas veces al pensar en contratos e interfaces nos vamos inmediatamente a pensar en servicios, pero pasa lo mismo con conceptos más “tangibles” dentro de nuestras aplicaciones. Si parte de nuestro sistema sólo necesita ciertos datos y operaciones de un cliente, y otra parte del sistema necesita datos y operaciones diferentes, aunque en la vida real sea la misma entidad (la empresa cliente), seguramente sea buena idea separarlo en dos contratos distintos y, posiblemente, en dos implementaciones distintas también.

Conclusiones

En cuanto te alejas un poco del código, el principio de segregación de interfaces consiste, básicamente, en simplificar al máximo las dependencias entre componentes, haciéndolas lo más específicas y focalizadas posibles.

La principal ventaja de esto es que vamos a conseguir componentes menos acoplados ya que dependerán de roles más específicos, y eso nos facilitará evolucionarlos por separado y aumentará la flexibilidad del sistema.

Debemos tener en cuenta que si al final esos roles son implementados por un único objeto, aunque hayamos independizado los contratos seguiremos teniendo acoplamiento a través de la implementación. Si optamos por implementaciones separadas para cara rol, el riesgo que asumimos es acabar con un código poco cohesionado más complicado de enteder y mantener.

Como siempre, cada caso merece una consideración aparte, pero si aplicas el sentido cómun y comprendes en qué se fundamentan los principios que estás aplicando, podrás tomar mejores decisiones.

Fotografía por Natalia Rivera con licencia CC BY 2.0

Posts relacionados:

  1. Interfaces marcadoras, atributos y convenciones
  2. Interfaces + Extension Methods = Protocolos de Clojure

» Leer más, comentarios, etc...

Blog Bitix

Certificado SSL, de empresa, «wildcard» y de validación extendida

April 25, 2015 10:00 AM

Usar un protocolo seguro garantiza de que los datos intercambiados entre el cliente y el servidor no son leídos ni modificados por una tercera parte además de verificar que la comunicación se está realizando entre las partes que dicen ser. Para usar un protocolo seguro como SSL/TLS debemos disponer de con certificado, con OpenSSL podemos generar y convertirlo al formato que deseemos, sin embargo, para que el usuario tenga garantía de que el certificado es válido este se debe estar firmado por una autoridad de certificación (CA) en la que confíe, generalmente con una de las autoridades de certificación cuyos certificados están preinstalados en el navegador web (en Firefox podemos verlos en Preferencias > Avanzado > Certificados > Ver certificados), los certificados autofirmados son útiles para hacer pruebas pero no son válidos para los usuarios. En este artículo comentaré que tipos de certificados hay y donde podemos obtener o comprar un certificado digital firmado por una CA que sea de confianza para el usuario.

Los navegadores suelen indicar que se está usando una comunicación segura cuando en la barra de direcciones se muestra un candado y se está usando el protocolo https. Además, haciendo clic en el candado se pueden ver los detalles del certificado usado por el servidor para la comunicación cifrada.

Sin embargo, para proporcionar más seguridad y garantía de que como usuarios nos estamos comunicando con el servidor que creemos sin examinar el certificado algunos certificados permiten mostrar también en la barra de direcciones un recuadro verde con el nombre de la entidad, el recuadro verde que solemos ver también en la barra de direcciones al acceder a algunos dominios y que es proporcionado por certificados de validación extendida.

Por otra parte los certificados SSL se generan para un dominio en concreto con lo que en principio se debería comprar un certificado por cada dominio en el que deseemos usar una comunicación segura. Sin embargo, para evitar comprar múltiples certificados para los diferentes dominios o subdominios podemos comprar un certificado wildcard que nos servirá para los subdominios (*.ejemplo.com) o un certificado multidominio (ejemplo.com, ejemplo.net, …) para como su nombre indica varios dominios. En los certificados de empresa se solicitan datos datos adicionales al adquirirlo y en los detalles del certificado aparece el nombre de la empresa (campo Organización (O) como en el caso de GitHub).

Los certificados wildcard y que muestran el recuadro verde son más caros pero pueden ser útiles sobre todo para una página de comercio electrónico, el recuadro verde añade más seguridad, seguramente mejore los ratios de conversión y evite suplantaciones de identidad o phising. Además, utilizar un protocolo seguro es un nuevo criterio que utiliza el buscador Google en su algoritmo para establecer el posicionamiento en la página de resultados. Con las intrucciones que incorporan los procesadores modernos el cifrado y descifrado de los datos no tiene por que significar un aumento de carga considerable para el servidor ni el cliente ni en dispositivos móviles.

¿Cómo obtener un certificado SSL firmado por una entidad raíz de certificación? Las entidades de registro de dominios aparte de dominios, hosting virtual o privado algunos permiten comprar certificados SSL. Uno de los que conozco que permite comprar certificados SSL, de empresa, con recuadro verde o wildcard es DonDominio, otro es Arsys.

En el caso de DonDominio dependiendo de la entidad emisora del certficado que deseemos variará el precio, también si queremos que tenga validación extendida o sea wildcard. En el caso de un certificado SSL simple que valide solo el dominio es de unos 5 €, de validación de empresa unos 28 €, un certificado de validación extendida con recuadro verde desde unos 126 € y un certificado wildcard de desde unos 75 €. Estos son precios desde, diferentes opciones pueden salir bastante más caras y hay que tener en cuenta que son para una validez de una año, al igual que los dominios hay que renovar su uso.

Si no necesitamos el recuadro verde ni un certificado wildcard una opción interesante es obtener uno gratis a través de Lets Encrypt. Esta nueva entidad de certificación nos permitirá obtener uno sin coste, de forma sencilla y automatizada, detrás de esta entidad están organizaciones como Linux Foundation, Mozilla o CISCO.

Una vez obtenido el certificado debemos instalarlo en el servidor, en el artículo Configurar SSL en un servidor Tomcat JBoss, Wildfly, Lighttpd, nginx o Apache comento como usar un certificado en los principales servidores web y de aplicaciones. Si necesitamos un formato concreto podemos convertir el certificado con OpenSSL.

Referencia:
Generar y convertir claves y certificados con OpenSSL
Configurar SSL en un servidor Tomcat, JBoss, WildFly, Lighttpd, nginx o Apache
Qué es un certificado SSL y por qué debería importarte

» Leer más, comentarios, etc...

Blog Bitix

Nueva visita a 5+ opciones de «hosting» para aplicaciones

April 24, 2015 08:00 PM

Hace tres años escribí un artículo sobre varias opciones de hosting que disponemos para hospedar aplicaciones o páginas web. En ese artículo comentaba varias posibilidades desde un servidor propio usando una Raspberry Pi, proveedores de hosting, Amazon EC2, Google App Engine, Jelastic, OpenShift, AppFog, Cloud Foundry (VMWare), Heroku, Azure (Microsoft) y Google Compute (Google Cloud Platform). Pasado todo este tiempo en este artículo revisaré las opciones de hospedaje con algunas nuevas que en su momento no conocía, otras siguen siendo totalmente válidas.

Servidor propio

Si queremos una opción para algo simple y para nosotros mismos podemos utilizar un servidor propio usando como hardware por ejemplo una Raspberry Pi 2 Model B, con la segunda versión de este miniordenador que ahora tiene 4 núcleos y 1 GiB de memoria puede servirnos para cantidad de cosas interesantes. Además de la Raspberry Pi 2 hay multitud de dispositivos similares, en la página de Arch Linux ARM podemos ver los modelos soportados en esta distribución. Otro modelo destacado es la Cubox-i también con 4 núcleos y en el modelo más potente con 2 GiB de memoria aunque bastante más caro que la Raspberry Pi.

Uno de los usos para los que nos puede servir es para albergar con ownCluod tu información personal como archivos, fotos, calendarios, contactos pudiendo acceder desde cualquier dispositivo ya sea el ordenador personal, teléfono inteligente o dispositivo móvil. Su punto a favor es que tus datos están bajo tu control. Aunque esta opción es posible se puede optar por otras opciones que comento más adelante, uno de los motivos es que no es fiable el servicio que podemos ofrecer ya que depende la conexión a internet que podemos tener en nuestra casa y por los cortes de corriente que se pueden producir o los fallos en el hardware que puede provocar que perdamos los datos si no tenemos copias de seguridad.

Con esta opción tendremos un control total del software instalado sin ninguna limitación, sin embargo, si necesitamos escalar la aplicación tendremos más dificultades si necesitamos comprar hardware nuevo.

Raspberry Pi

Proveedor de hosting

La siguiente opción a considerar puede ser un servidor ofrecido por muchos registradores de dominios. Si el tráfico del servicio que planeamos ofrecer no es muy alto (y así serán en los inicios) es una opción adecuada. En la mayoría de las opciones de este tipo estaremos limitados en el lenguaje de programación que podremos usar generalmente PHP o la base de datos generalmente MySql.

Mucha gente usa esta opción para albergar su bitácora con WordPress que incluso para un tráfico considerable es suficiente, por supuesto es válida para las empresas que quieran tener presencia en internet aunque solo sea para incluir su dirección teléfono de contacto y una breve descripción y los productos que ofrece. Para estos usos el coste de esta opción puede estar entre 1€/mes y 20€/mes aparte del coste del dominio.

Hay multitud de proveedores algunos de los más conocidos son DonDominio, Arsys, Piensa Solutions, … pero ninguno de estos y en otros casos muy pocos ofrecen hosting para aplicaciones de la plataforma Java. No tendremos libertad en la tecnología que usamos ni siquiera en las versiones de la misma.

Amazon EC2, Azure, Google Cloud

Si la aplicación o proyecto crece las nubes de Amazon EC2, Azure y Google Cloud se adaptan a las necesidades que tengamos ahora y, mejor aún, en el futuro. Y si en un futuro es necesario nos proporcionan flexibilidad pudiendo ampliar o reducir los recursos consumidos. Son algo más caras que otras opciones pero por lo que ofrecen son buenas opciones, por ejemplo, Amazon EC2 ofrece varios servicios que enriquecen su oferta de infraestructura como servicio (IaaS, Infrastructure as a service). En estas opciones de IaaS tendremos gran control sobre el software que instalamos, deberemos tener en cuenta que los datos que maneja la aplicación estarán hospedados en los sistemas de la nube elegida.

En estas opciones IaaS tenemos libertad de elegir el lenguaje de programación que queremos emplear para en la aplicación ya sea Java, PHP, Python, C# o Ruby, … también tendremos libertad en la base de datos MySQL, PosgreSQL, Redis, … o en el servidor de aplicaciones o servidor web.

En Amazon EC2 el coste puede variar dependiendo de los recursos que reservemos, para una aplicación mediana una instancia t1.small con 2GiB de memoria reservada previamente y 50 GiB de espacio de disco SSD durante 3 años cuesta unos 332.00€ por reservar la instancia durante ese periodo y unos 2€ adicionales al mes, a esto deberemos sumar el coste del dominio. En la calculadora de Amazon EC2 podemos estimar el coste según los recursos que necesitemos.

Amazon Web Services Microsoft Azure Google Cloud

Linode, Digital Ocean

Algunas opciones totalmente válidas para muchos casos que también nos proporcionan flexibilidad son Linode y Digital Ocean, también entran dentro de la categoría de IaaS. El coste de los planes ofrecidos por cada una de estas es muy sencillo en Linode empieza desde los 10€/mes hasta los 80€/mes y en Digital Ocean desde los 5€/mes pasando por los 80€/mes hasta los 640€/mes. Salvo los planes de volúmenes altos de Digital Ocean los precios son similares a Linode según las características disponibles en ambos.

Estas opciones son totalmente válidas tanto para proyectos personales como un blog con WordPress como para proyectos profesionales. La nube de Amazon ofrece multitud de servicios que en algunos casos son útiles pero si no nos son necesarios las opciones comentadas en este apartado serán suficientes y algo más baratas.

Linode Digital Ocean

Otras

Hay otro tipo de opciones conocidas como plataforma como servicio (PaaS, Platform as a service) como OpenShift, AppFog, Cloud Foundry, Heroku. De entre estas destacaré OpenShift ya que proporciona una capa gratuita con la que tendremos lo que ellos denominan gears, nos ofrecen gratuitamente 3 con 512 MiB de memoria y 1 GiB de espacio en disco. Esta opción es la que he utilizado para construir Blog Stack, en el artículo Arquitectura y hospedaje de Blog Stack detallo técnicamente el proyecto en el que el único coste es el dominio (12€/año).

Notas finales

Las opciones son múltiples para hospedar nuestra aplicación. Si se trata de algo para uso personal una Raspberry Pi pueda valernos, si se trata de una web presencial estática que no requiera programación un proveedor de hosting será suficiente. Si queremos tener flexibilidad y más libertad en las herramientas del proyecto las nubes de Amazon, Azure o Google nos servirán. Si no necesitamos todos los servicios de los anteriores Linode o Digital Ocean ofrecen unos planes de precios muy sencillos y previsibles. Finalmente están los PaaS que permiten desentendernos de la infraestructura y centrarnos en la aplicación.

Referencia:
5 opciones de hosting para aplicaciones Java
Arquitectura y hospedaje de Blog Stack

» Leer más, comentarios, etc...

Poesía Binaria

Creando un modelo sencillo en Magento (paso a paso y con ejemplos)

April 21, 2015 08:51 AM

Anteriormente vimos cómo crear un plugin para Magento y cómo crear un controlador para Magento. Ahora le toca el turno al modelo. Intentaré poner con todo detalle todo el proceso.
Por eso, vamos a editar app/code/local/NuestraEmpresa/nuestroPlugin/etc/config.xml, en nuestro ejemplo app/code/local/Poesia/HolaMundo/etc/config.xml para indicar que vamos a crear modelos y las conexiones de base de datos que se deben utilizar para el acceso. He pegado el archivo entero, pero indico con un comentario las novedades:

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
<?xml version="1.0" encoding="UTF-8"?>
<!--
Opcionalmente podemos poner un comentario contando qué hace nuestro módulo, nuestra web,
información sobre la licencia, etc
-->
<config>
    <modules>
        <Poesia_HolaMundo> <!-- namespace_modulo -->

            <!-- Esta es la versión de nuestro módulo -->
            <version>0.0.1</version>

        </Poesia_HolaMundo>
    </modules>

    <frontend>
      <!-- Lo que necesitamos para utilizar los controladores -->
      <routers>
    <holaMundo>
      <use>standard</use>
      <args>
        <module>Poesia_HolaMundo</module>
        <frontName>hm</frontName>
      </args>
    </holaMundo>
      </routers>
      <!-- Lo que necesitamos para utilizar los controladores -->
      <!-- Lo que necesitamos para el layout y las plantillas -->
      <layout>
    <updates>
      <holamundo>
        <file>poesia.xml</file>
      </holamundo>
    </updates>
      </layout>
      <!-- Lo que necesitamos para el layout y las plantillas -->
    </frontend>
    <!-- Necesario para informar a Magento sobre nuestro modelo -->
    <global>
      <models>
    <holamundo>
        <class>Poesia_Holamundo_Model</class>
        <resourceModel>holamundo_resource</resourceModel>
    </holamundo>
    <holamundo_resource>
        <class>Poesia_HolaMundo_Model_Resource</class>
        <entities>
        <pventamod>
            <table>poesia_postvtable</table>
        </pventamod>
        </entities>
    </holamundo_resource>
      </models>
      <resources>
        <holamundo_setup>
          <setup>
            <module>Poesia_HolaMundo</module>
          </setup>
          <connection>
            <use>core_setup</use>
          </connection>
        </holamundo_setup>
        <holamundo_write>
          <connection>
            <use>core_write</use>
          </connection>
        </holamundo_write>
        <holamundo_read>
          <connection>
            <use>core_read</use>
          </connection>
        </holamundo_read>
      </resources>

    </global>
    <!-- Necesario para informar a Magento sobre nuestro modelo -->
</config>

Ahora creamos el modelo (le intento dar a todo nombres diferentes para que veamos qué es cada cosa). Más o menos esto son algunas definiciones básicas para empezar a trabajar.
[modelo y todo eso luego]

Creamos la tabla en la base de datos

Aunque se puede hacer todo directamente, yo prefiero crear la tabla a mano primero para ir haciendo pruebas y asegurarme de que todo va funcionando bien. Más adelante crearemos un script que se encargará de generar la tabla, pero, por ahora la crearemos a mano en MySQL para asegurarnos de que todo encaja. Supondremos que el prefijo de las tablas de Magento es “mage_”, que viene por defecto, lo podemos ver en app/etc/local.xml (config.global.resources.db.table_prefix). Lo siguiente, lo ejecutaremos directamente en nuestro servidor de base de datos, ya sea por consola, o a través de PHPMyAdmin o similares.

1
2
3
4
5
6
7
CREATE TABLE mage_poesia_postvtable (
   table_id BIGINT NOT NULL AUTO_INCREMENT,
   user_id BIGINT,
   user_email VARCHAR(192),
   comment TEXT,
   PRIMARY KEY(table_id)
) ENGINE=InnoDB DEFAULT CHARSET='UTF8';

Como en el XML dijimos que el nombre de la tabla era poesia_postvtable, le ponemos el prefijo delante al crearla.

Y como estamos probando y sólo queremos algunos datos sin sentido para saber que todo va bien:

1
2
3
INSERT INTO mage_poesia_postvtable VALUES (NULL, 1, 'usuario1@servidor.ext', 'Never forget the WHERE');
INSERT INTO mage_poesia_postvtable VALUES (NULL, 100, 'user2@server.ext', 'What does the fox say?');
INSERT INTO mage_poesia_postvtable VALUES (NULL, 123, 'usuario3@server.ext', 'The day the routers die');

Crear estructura de ficheros del modelo

Ahora toca el turno de crear ficheros y directorios. Para ello, vamos a crear dentro del directorio de nuestro plugin el siguiente árbol de directorios (en mi caso app/code/local/Poesia/HolaMundo/):

  • Model (aquí irán todos los modelos)
    • Resource (dentro de los modelos tendremos los recursos)
      • Pventamod (será el correspondiente al modelo que vamos a hacer)

Ahora crearemos los archivos necesarios para utilizarlos:
app/code/local/Poesia/HolaMundo/Model/Pventamod.php

1
2
3
4
5
6
7
8
9
<?php

class Poesia_HolaMundo_Model_Pventamod extends Mage_Core_Model_Abstract
{
  public function _construct()
  {
    parent::_construct();
    $this->_init('holamundo/pventamod');
  }

app/code/local/Poesia/HolaMundo/Model/Resource/Pventamod.php

1
2
3
4
5
6
7
8
<?php
class Poesia_HolaMundo_Model_Resource_Pventamod extends Mage_Core_Model_Resource_Db_Abstract
{
  protected function _construct()
  {
    $this->_init('holamundo/pventamod', 'table_id');
  }
}

app/code/local/Poesia/HolaMundo/Model/Resource/Pventamod/Collection.php

1
2
3
4
5
6
7
8
9
10
<?php

class Poesia_HolaMundo_Model_Resource_Pventamod_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
{
  public function _construct()
  {
    parent::_construct();
    $this->_init('holamundo/pventamod');
  }
}

¡¡Ya está listo nuestro modelo!!

Haciendo algo con el modelo

Ya podemos empezar a trabajar con nuestro modelo (no vamos a entrar en detalle en la utilización del ORM de Magento), por ejemplo, consultando información. Para ello, vamos a utilizar una vista (podemos utilizar una vista de nuestro controlador (holamundo), y hacer lo siguiente:
app/design/frontend/base/default/template/holamundo/test.phtml

1
2
3
4
5
6
7
8
9
10
<p>Ejemplo de vista de TEST</p>
<?php
    $model = Mage::getmodel('holamundo/pventamod');
    $primero = $model->load(1);
    echo get_class($primero);
    echo "ID: ". $primero->getTableId()."<br/>";
    echo "Userid: ".$primero->getUserId()."<br/>";
    echo "Email: ".$primero->getUserEmail()."<br/>";
    echo "Comentario: ".$primero->getComment()."<br/>";
?>

Auto-creación de tablas

Para facilitar al usuario a la hora de instalar plugins, facilitar las actualizaciones cuando las pasamos a producción y agilizar todo el proceso. Consiste en una serie de scripts para crear y actualizar la base de datos, que nos permitirá ejecutar cualquier sentencia SQL ante la presentación de una determinada versión de nuestro plugin.

Vamos a crear, dentro del directorio de nuestro plugin, el directorio sql, y dentro de éste holamundo_setup (igual que estaba puesto bajo resources en nuestro config.xml). Dentro crearemos un archivo llamado install-0.0.1.php (0.0.1 es la versión de nuestro plugin, que la pusimos cuando creamos el config.xml).

Nuestro archivo app/code/local/Poesia/HolaMundo/sql/holamundo_setup/install-0.0.1.php será el siguiente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

$installer = $this;

$installer->startSetup();

$installer->run("

CREATE TABLE IF NOT EXISTS `{$this->getTable('poesia_postvtable')}`(
   table_id bigint NOT NULL AUTO_INCREMENT,
   user_id BIGINT,
   user_email VARCHAR(192),
   comment TEXT,
   PRIMARY KEY(table_id)
) ENGINE=InnoDB DEFAULT CHARSET='utf8';
    "
);

$installer->run("INSERT INTO `{$this->getTable('poesia_postvtable')}`
   VALUES (NULL, 10, 'email@email.com', 'comentario de prueba')"
);

$installer->endSetup();

Podemos ver que para colocar el nombre de la tabla utilizamos $this->getTable(), esta función incluirá el nombre de la tabla con el prefijo del sistema. No es obligatorio poner un INSERT, yo lo he puesto para que el ejemplo funcione directamente.

Para comprobar que este script se ejecuta, sólo en pruebas, podemos introducir un die(‘Me ejecuto’) al principio del archivo de instalación. Así veremos que todo marcha bien.

Si vemos que, aunque borrando la base de datos, no se carga el script de instalación, podemos forzarlo haciendo que el sistema olvide nuestro plugin, accediendo a la base de datos y buscando la tabla “mage_core_resource” (de nuevo supongo que vuestro prefijo es mage_), ahí podemos hacer lo siguiente:

mysql [localhost] (magento) > SELECT * FROM mage_core_resource WHERE code LIKE ‘%holamundo%’;
+—————–+———+————–+
| code | version | data_version |
+—————–+———+————–+
| holamundo_setup | 0.0.1 | 0.0.1 |
+—————–+———+————–+
1 row in set (0.00 sec)

mysql [localhost] (magento) > DELETE FROM mage_core_resource WHERE code LIKE ‘%holamundo%’;
Query OK, 1 row affected (0.00 sec)

Tened cuidado por si borráis algo necesario, aquí sólo estamos borrando nuestro plugin, pero si hay algún plugin más, debemos utilizar su nombre completo.

Ahora cuando recarguemos alguna página de Magento, el plugin se instalará y de nuevo podremos ver la tabla en nuestra base de datos (y empezar a operar con ella). Si de hecho es la primera vez que esto se ejecuta en esta instalación de Magento, esto será transparente para el usuario.

Actualizando plugins en la base de datos

Cuando actualizamos un plugin que hemos creado, es común cambiar cosas en las tablas, imaginemos que hemos metido un campo más en la tabla, variado longitudes de campo, añadido claves, etc. Para eso tenemos los scripts de actualización. De la misma forma que los scripts de instalación, éstos estarán situados en app/code/local/NombreEmpresa/NombrePlugin/sql/recurso_setup/ y tendrán como nombre “upgrade-[versión.antigua]-[versión_nueva].php.

Ahora, nuestro plugin lo actualizamos a la versión 0.2.0 (antiguamente tenía la 0.0.1) entonces crearemos un script llamado:
app/code/local/Poesia/HolaMundo/sql/holamundo_setup/upgrade-0.0.1-0.2.0.php

1
2
3
4
5
6
7
8
9
10
11
12
<?php

$installer = $this;

$installer->startSetup();

$installer->run("

ALTER TABLE `{$this->getTable('poesia_postvtable')}`
   ADD COLUMN tipo BIGINT NOT NULL;"
);

$installer->endSetup();

y con esto añadimos la columna tipo que podremos utilizar a partir de este instante.

Foto: Susana Fernandez (Flickr CC-By)

The post Creando un modelo sencillo en Magento (paso a paso y con ejemplos) appeared first on Poesía Binaria.

» Leer más, comentarios, etc...

Variable not found

Precompilación de vistas en MVC 6, powered by Roslyn

April 21, 2015 07:05 AM

ASP.NET MVCSeguro que ya todos conocéis la fórmula mágica mediante la cual podemos activar la compilación de vistas en versiones de ASP.NET MVC anteriores a la 6. Los que no sepáis de qué os hablo, podéis echar un vistazo a este vídeo que publiqué en el blog de CampusMVP hace algún tiempo: VIDEO: Activar la compilación de vistas en ASP.NET MVC.

MvcBuildViewsMuy resumidamente, el asunto consistía en editar manualmente el archivo de proyecto de Visual Studio, con extensión .csproj, y establecer el contenido del tag <MvcBuildViews> a true.


A partir de ese momento, y tras recargar el proyecto, Visual Studio comienza a compilar las vistas como parte del proceso de construcción de la solución. Obviamente este proceso tardará un poquito más, pero al menos de esta forma tenemos la seguridad de que las vistas no van a explotar en tiempo de ejecución por errores de compilación.

Por otra parte, sabemos que el nuevo ASP.NET 5 viene con bastantes novedades, entre las que destaca la desaparición y de muchos archivos clásicos, como el web.config, el global.asax… y también el .csproj. Y dado que era en éste donde activábamos la compilación de vistas, ¿dónde ha ido a parar ahora esa característica?

Pero antes de continuar, el tradicional disclaimer: en estos momentos ASP.NET 5 y MVC 6 se encuentran en fase de desarrollo, y todo lo que digamos a partir de ahora puede quedar en agua de borrajas.

Bien, pues la respuesta a la pregunta anterior la encontramos en Roslyn. Creo que todavía nos falta mucho hasta llegar a comprender el potencial y las posibilidades que se abren tras la llegada de este nuevo compilador, pero que lo que vamos a ver ahora es seguro un buen ejemplo.

image
Resulta que con Roslyn es posible introducir código personalizado en el momento de la compilación, y MVC aprovecha esta característica para insertar la compilación de las vistas Razor en el proceso.

Si observamos un proyecto ASP.NET 5 generado usando la plantilla  de Visual Studio 2015, podemos ver que existe un archivo RazorPreCompilation.cs en la carpeta /Compiler/Preprocess, como se muestra en la captura de pantalla adjunta.

En su interior, encontramos una clase llamada RazorPreCompilation, que hereda de la clase suministrada por MVC RazorPreCompileModule, que a su vez implementa el interfaz ICompileModule proporcionado por Roslyn. El código que trae esta clase por defecto es el siguiente:

RazorPreCompilation

Durante la compilación, Roslyn busca en esa carpeta exacta módulos de preproceso, que en la práctica son archivos con clases que implementan ICompileModule, y les cede el control antes y después de compilar, dándoles la oportunidad de introducir entradas al árbol sintáctico resultante de la compilación o mensajes de diagnóstico y error al resultado.

La clase que añadimos a nuestra aplicación en esa carpeta puede tener cualquier nombre, pero lo importante es que herede de RazorPreCompilation (definido en Microsoft.AspNet.Mvc ), pues se trata de la implementación de ICompileModule que utiliza internamente el precompilador de vistas de Razor para generar los resultados.

Resumiendo, lo único que necesitamos para que las vistas de nuestra aplicación se precompilen es crear una clase cualquiera que herede de RazorPreCompileModule en la carpeta /Compiler/Preprocess del proyecto, y Roslyn se encargará del resto :)

Publicado en Variable not found.

» Leer más, comentarios, etc...

proyectos Ágiles

Agile en la revista Emprendedores

April 20, 2015 09:23 PM

La divulgación de Agile y Lean sigue llegando al gran público !!

Este mes, la revista Emprendedores incluye el Dossier "Haz tu negocio más eficiente", donde se comenta la necesidad de la Agilidad en la respuesta al mercado y se identifican los desperdicios en el trabajo desde una perspectiva Lean.

 revista-emprendedores-haz-tu-negocio-mas-eficiente

 

Artículos relacionados

» Leer más, comentarios, etc...

Arragonán

Semana 358

April 20, 2015 12:45 PM

Hoy llevo síndrome post Startup Open Space. Que estoy bastante machacado aún, vamos. La verdad que creo que salió todo bastante bien, y este año lo disfruté incluso más que en la edición anterior, se notó que a nivel organizativo lo llevábamos mucho más rodado.

Cachirulo Crew

Cachirulo Crew. De izquierda a derecha: @danieltwal @torhector2 @lauralacarra @pablojimeno @dani_latorre @gimenete @aaromnido. Ausentes en la foto: @rrasmo @superwillyfoc @olmeras. Foto de @capitangolo.

A nivel de contenido, me gustaron bastante las sesiones a la que asistí, con el nivel que había entre los asistentes se podía presuponer que la cosa no saliera mal. También, como de costumbre, me lo pasé genial con las charlas entre las cervezas de ambas noches y ejerciendo un poco de guía turístico para los foranos :P. En fin, ayer viendo el hashtag en twitter parece que la cosa gustó bastante, ahora nos queda recoger algo de feedback para ver cómo podemos mejorar para la siguiente.

Y aunque el sosZ15 ocupara gran parte de mi semana, también hice más cosas. Por ejemplo: estuve grabando con los amigos del videoblog Qué pasa Co!, que han empezado a hacer entrevistas y estuve ahí para hablar de Mosica.

Continuamos una semana más con SenpaiDevs, seguimos trabajando en el proyecto del calendario y parece que pronto podremos dar por acabada la primera versión. Esta semana ya he empezado a notar que se está haciendo un grupete majo al haber poco a poco más confianza entre todos; además parece que empieza a picar el gusanillo de participar en las comunidades locales (uno de nuestros objetivos con esto).

Cerrando la semana tuvimos una reunión con una empresa aragonesa para ver si podíamos colaborar para echarles una mano en temas de testing, que están empezando con ello y sienten que necesitarán ayuda.

En proyectos actuales:

  • Ya tengo casi lista la app móvil de Mosica. Mientras en la web hice alguna pequeña modificación a nivel de API y modifiqué el mapa de la página de un concierto a los basemaps de cartodb.
  • Continué trabajando en One-stop, exponiendo un API para ofrecer un método de importación de datos automatizada y seguir desarrollando algunas funcionalidades en el backoffice.
  • Aunque retomé mi trabajo con Bichomanía, apenas pude adelantar, esta semana tocará ponerse las pilas.

Buena semana.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 195

April 20, 2015 07:15 AM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, espero que os resulten interesantes :-)

.Net

ASP.NET

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Otros

Publicado en Variable not found

» Leer más, comentarios, etc...

Koalite

Recopilatorio sobre modelos de dominio

April 20, 2015 05:06 AM

Un problema de muchos blogs, y en especial de éste, es que el contenido se va generando según le apetece al autor, o sea a mi, y eso hace que a veces la organización no sea todo lo buena que a uno le gustaría, especialmente si no eres un lector frecuente y caes por aquí de casualidad. Para intentar paliar ese problema, he decidido escribir este post para recopilar lo que he escrito sobre cómo diseñar modelos de dominio y así poder encontrar ese material más fácilmente.

Conceptos básicos

En el post sobre diseño de modelos veíamos distintos tipos de modelos y hablábamos sobre la evolución desde modelos relacionales a modelos orientados a objetos pasando por modelos anémicos. Merece la pena echarle un vistazo, aunque sólo sea por leer los distintos puntos de vista que se exponen en los comentarios.

Aunque no es un requisito indispensable, muchas veces se asocia tener un modelo de dominio con aplicar DDD como proceso. En ese sentido, deberías plantearte si puedes hacer DDD sin un experto de dominio. Nuevamente la discusión de los comentarios es más interesante que el post.

Cómo construir un modelo de dominio

Para diseñar un modelo de dominio, es necesario identificar los aggregate roots alrededor de los cuales construiremos el modelo, y buscar formas de enriquecerlo con comportamiento para huir de un modelo anémico.

En ese sentido, podemos quitar lógica de servicios para pasarla a entidades y value objects o darle más peso a nuestros value objects evitando usar tipos enumerados.

También podemos utilizar eventos de dominio para desacoplar el modelo de sistemas externos, aunque eso sí, debemos tener cuidado de cómo gestionamos las transacciones al usar eventos de dominio.

Conceptos sobre orientación a objetos

Al final, el modelo de dominio no deja de ser un diseño orientado a objetos (al menos en estos posts), por lo que los principios básicos de diseño orientado a objetos se aplican al modelo.

Por ello viene bien comprender lo que son la cohesión y el acoplamiento, cómo aplicar la Ley de Demeter, el principio de Tell, Don’t Ask (y los riesgos asociados) o qué significa realmente la inversión de dependencias. Tampoco está de más que cuando empieces a dejarte llevar por la fiebre polimórfica orientada a objetos, seas consciente de que no siempre se puede abstraer todo, y que si algo no lo puedes abstraer, mejor hazlo explícito.

Podemos conseguir un modelo más sólido si aprovechamos técnicas como el diseño por contrato, especialmente para tener claros los invariantes que debemos mantener dentro de cada clase que forma parte del modelo. Esto puede influir en el tipo de colecciones que usamos en cada momento. A veces el tema de los invariantes, precondiciones y demás se confunde con las necesidades de securizar, autorizar y validar.

Persistencia

Por supuesto he escrito mi buena cuota de posts sobre persistencia (¿por qué nos gustará tanto darle vueltas a lo mismo?).

Puedes empezar por ver las diferencias entre un ORM, un Micro-ORM y ADO.NET y ver cuándo usar cada uno. Si te quedan dudas, en este post explico por qué sigo usando un ORM, aunque sea algo que empieza a estar pasado de moda en ciertos círculos.

Cuando utilizas un modelo de dominio, el patrón de diseño por excelencia para gestionar la persistencia es el repositorio, en cualquiera de sus variantes: el repositorio genérico, el repositorio concreto, el repositorio implícito (usar directamente el ORM) o la separación de lecturas y escrituras. La tendencia actual es cada vez más no utilizar repositorios y usar directamente el ORM, pero los repositorios como abstracción también aportan un valor que puede ser interesante.

Una nota final

Es posible que me deje algún post que pudiera tener cabida en este resumen (si me avisas en los comentarios, lo añado), pero creo que esta “selección” incluye los posts que más te pueden servir para diseñar un bueno modelo de dominio.

No tengo por costumbre reescribir posts antiguos, y esta vez tampoco lo he hecho, por lo que es posible que si lees varios de los posts citados notes algunas inconsistencias o, directamente, contradicciones. Es normal. Todo evoluciona con el tiempo y la forma de ver las cosas cambia con la experiencia. Tómatelo como un consejo más: lo que hoy piensas que es la mejor opción, mañana puede dejar de parecértelo.

Posts relacionados:

  1. Desacoplar modelos con Eventos de Dominio
  2. Crear modelos más ricos quitando lógica de los servicios
  3. Eventos de Dominio y Transacciones

» Leer más, comentarios, etc...

Navegapolis

Fórmula para reducir el coste laboral de los programadores

April 17, 2015 07:06 AM

coste laboralEl coste laboral está en relación directa con el salario: coste = salario, pero con un modificador importante: "valor que aporta la persona".

coste = salario / valor.

En la manufactura industrial, y demás trabajos que aplican ingeniería de procesos para garantizar la calidad del resultado,  y que la diferencia entre las personas no resulte significativa, al factor "valor" se le puede considerar una constante.
Además la ingeniería de procesos minimiza la pérdida de know how en la rotación de personal, así que si no escasean los "operarios" en el mercado laboral, se puede simplificar la fórmula y dejarla en "coste = salario", considerando más barato al que menos cobra.

Sin embargo, en la construcción de software, la diferencia de valor aportado entre los buenos y los mejores es enorme. Tan grande que resulta miope mirar al factor "salario" para reducir el coste.
Y por supuesto a los malos y mediocres, ni los considere. A ningún precio; porque disparan el coste al no aportar valor, o aportarlo negativo, porque degradan y complican el código tocan.

 

» Leer más, comentarios, etc...

Juanjo Navarro

Cursos de Google en Udacity

April 16, 2015 03:34 PM

Google anunció la semana pasada un curso sobre rendimiento en Android en la plataforma de enseñanza online Udacity.

Udacity y Google

El curso se estructura en cuatro bloques, que trata problemas de rendimiento respectivamente en renderizado, proceso, memoria y batería.

Este curso se une además a los varios que Google tiene en la plataforma Udacity (y que se pueden consultar desde el catálogo de cursos, filtrando por el listado de la izquierda). Los cursos van desde temas genéricos de diseño responsive hasta tecnologías propias de Google como este de Android (hay varios más sobre Android) o el App Engine (tienen cursos de Python y Java en el App Engine).

» Leer más, comentarios, etc...

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Sponsors

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin