CCNA 200-125, Cisco CCNA Cisco Certified Network Associate CCNA (v3.0) Dump 100-105 Answer, Cisco ICND1 Answer, 100-105 Cisco Interconnecting Cisco Networking Devices Part 1 (ICND1 v3.0) Answer Cisco 200-310, CCDA 200-310 Designing for Cisco Internetwork Solutions, Cisco 200-310 PDF Cisco CCDP 300-101, 300-101 Implementing Cisco IP Routing (ROUTE v2.0) Exam 300-075, CCNP Collaboration 300-075 Exam Dump, Implementing Cisco IP Telephony & Video, Part 2(CIPTV2) Exam Dump 810-403 Questions, Cisco Business Value Specialist 810-403 Selling Business Outcomes Questions CCNA Collaboration 210-060, Cisco Implementing Cisco Collaboration Devices (CICD) Practice 210-260 Dump, Cisco CCNA Security Dump, 210-260 Implementing Cisco Network Security Dump PMI PMP, PMP PMP Project Management Professional, PMI PMP Answer ISC ISC Certification CISSP, CISSP Certified Information Systems Security Professional PDF 70-534, Microsoft Specialist: Microsoft Azure 70-534 Exam, Architecting Microsoft Azure Solutions Exam 101 Dumps, F5 Certification 101 Application Delivery Fundamentals Dumps Microsoft Office 365 70-346, Microsoft Managing Office 365 Identities and Requirements Questions 2V0-621D Practice, VMware VCP6-DCV Practice, 2V0-621D VMware Certified Professional 6 ¡§C Data Center Virtualization Delta Beta Practice Cisco 300-206, CCNP Security 300-206 Implementing Cisco Edge Network Security Solutions, Cisco 300-206 Dump Cisco CCNP Collaboration 300-070, 300-070 Implementing Cisco IP Telephony & Video, Part 1(CIPTV1) Answer 300-207, CCNP Security 300-207 PDF, Implementing Cisco Threat Control Solutions PDF 1Z0-062 Exam, Oracle Database 1Z0-062 Oracle Database 12c: Installation and Administration Exam CompTIA Network+ N10-006, CompTIA CompTIA Network+ Dumps 300-115 Questions, Cisco CCDP Questions, 300-115 Implementing Cisco IP Switched Networks (SWITCH v2.0)Questions Microsoft 070-346, Microsoft Office 365 070-346 Managing Office 365 Identities and Requirements, Microsoft 070-346 Practice Cisco CCDP 300-320, 300-320 Designing Cisco Network Service Architectures Dump 640-916, CCNA Data Center 640-916 Answer, Introducing Cisco Data Center Technologies Answer 648-232 PDF, APE 648-232 Cisco WebEx Solutions Design and Implementation PDF CCNA Wireless 200-355, Cisco Implementing Cisco Wireless Network Fundamentals Exam 2V0-621D Practice, VMware VCP6-DCV Practice, 2V0-621D VMware Certified Professional 6 ¡§C Data Center Virtualization Delta Beta Practice Cisco CCDP 300-101, 300-101 Implementing Cisco IP Routing (ROUTE v2.0) Exam 648-232 PDF, APE 648-232 Cisco WebEx Solutions Design and Implementation PDF CompTIA Network+ N10-006, CompTIA CompTIA Network+ Dumps PMI PMP, PMP PMP Project Management Professional, PMI PMP Answer 100-105 Answer, Cisco ICND1 Answer, 100-105 Cisco Interconnecting Cisco Networking Devices Part 1 (ICND1 v3.0) Answer 101 Dumps, F5 Certification 101 Application Delivery Fundamentals Dumps CCNA Collaboration 210-060, Cisco Implementing Cisco Collaboration Devices (CICD) Practice 300-207, CCNP Security 300-207 PDF, Implementing Cisco Threat Control Solutions PDF Cisco CCDP 300-320, 300-320 Designing Cisco Network Service Architectures Dump 2V0-621D Practice, VMware VCP6-DCV Practice, 2V0-621D VMware Certified Professional 6 ¡§C Data Center Virtualization Delta Beta Practice Cisco CCDP 300-101, 300-101 Implementing Cisco IP Routing (ROUTE v2.0) Exam 648-232 PDF, APE 648-232 Cisco WebEx Solutions Design and Implementation PDF CompTIA Network+ N10-006, CompTIA CompTIA Network+ Dumps PMI PMP, PMP PMP Project Management Professional, PMI PMP Answer Microsoft 070-346, Microsoft Office 365 070-346 Managing Office 365 Identities and Requirements, Microsoft 070-346 Practice CCNA 200-125, Cisco CCNA Cisco Certified Network Associate CCNA (v3.0) Dump Cisco CCNP Collaboration 300-070, 300-070 Implementing Cisco IP Telephony & Video, Part 1(CIPTV1) Answer 70-534, Microsoft Specialist: Microsoft Azure 70-534 Exam, Architecting Microsoft Azure Solutions Exam 810-403 Questions, Cisco Business Value Specialist 810-403 Selling Business Outcomes Questions 100-105 Answer, Cisco ICND1 Answer, 100-105 Cisco Interconnecting Cisco Networking Devices Part 1 (ICND1 v3.0) Answer 101 Dumps, F5 Certification 101 Application Delivery Fundamentals Dumps CCNA Collaboration 210-060, Cisco Implementing Cisco Collaboration Devices (CICD) Practice 300-207, CCNP Security 300-207 PDF, Implementing Cisco Threat Control Solutions PDF Cisco CCDP 300-320, 300-320 Designing Cisco Network Service Architectures Dump CCNA Wireless 200-355, Cisco Implementing Cisco Wireless Network Fundamentals Exam 300-075, CCNP Collaboration 300-075 Exam Dump, Implementing Cisco IP Telephony & Video, Part 2(CIPTV2) Exam Dump 300-115 Questions, Cisco CCDP Questions, 300-115 Implementing Cisco IP Switched Networks (SWITCH v2.0)Questions Cisco 300-206, CCNP Security 300-206 Implementing Cisco Edge Network Security Solutions, Cisco 300-206 Dump ISC ISC Certification CISSP, CISSP Certified Information Systems Security Professional PDF 1Z0-062 Exam, Oracle Database 1Z0-062 Oracle Database 12c: Installation and Administration Exam Cisco 200-310, CCDA 200-310 Designing for Cisco Internetwork Solutions, Cisco 200-310 PDF 210-260 Dump, Cisco CCNA Security Dump, 210-260 Implementing Cisco Network Security Dump 640-916, CCNA Data Center 640-916 Answer, Introducing Cisco Data Center Technologies Answer Microsoft Office 365 70-346, Microsoft Managing Office 365 Identities and Requirements Questions 1Z0-062 Exam, Oracle Database 1Z0-062 Oracle Database 12c: Installation and Administration Exam Cisco 200-310, CCDA 200-310 Designing for Cisco Internetwork Solutions, Cisco 200-310 PDF 210-260 Dump, Cisco CCNA Security Dump, 210-260 Implementing Cisco Network Security Dump 640-916, CCNA Data Center 640-916 Answer, Introducing Cisco Data Center Technologies Answer Microsoft Office 365 70-346, Microsoft Managing Office 365 Identities and Requirements Questions Microsoft 070-346, Microsoft Office 365 070-346 Managing Office 365 Identities and Requirements, Microsoft 070-346 Practice CCNA 200-125, Cisco CCNA Cisco Certified Network Associate CCNA (v3.0) Dump Cisco CCNP Collaboration 300-070, 300-070 Implementing Cisco IP Telephony & Video, Part 1(CIPTV1) Answer 70-534, Microsoft Specialist: Microsoft Azure 70-534 Exam, Architecting Microsoft Azure Solutions Exam 810-403 Questions, Cisco Business Value Specialist 810-403 Selling Business Outcomes Questions CCNA Wireless 200-355, Cisco Implementing Cisco Wireless Network Fundamentals Exam 300-075, CCNP Collaboration 300-075 Exam Dump, Implementing Cisco IP Telephony & Video, Part 2(CIPTV2) Exam Dump 300-115 Questions, Cisco CCDP Questions, 300-115 Implementing Cisco IP Switched Networks (SWITCH v2.0)Questions Cisco 300-206, CCNP Security 300-206 Implementing Cisco Edge Network Security Solutions, Cisco 300-206 Dump ISC ISC Certification CISSP, CISSP Certified Information Systems Security Professional PDF 642-996, 70-243, 70-462, 70-697, 9L0-012, 642-997, 70-331, 70-463, 70-980, 9L0-066, 642-889, 700-801, 70-417, 70-410 exam, 9A0-385, 642-732, 700-505, 70-413, 70-533, 98-366, 642-998, 70-332, 70-466, 70-981, 300-101 practice, 400-101 dumps, 70-345, 70-483, Cisco 200-105, 100-105, 640-911, 700-038, 70-410, 70-489, 840-425, 640-692, 648-232, 70-346, 70-486, 74-678, 640-875, 700-001, 70-347, 70-487, 77-420, 642-980, 700-803, 70-461, 70-680, 9A0-388, 640-878, 700-037, 70-384, 70-488, 810-403, 642-999, 70-333, 70-480, 712-50, ACCP-V6.2, 642-747, 700-702, 70-414, 70-534, 98-379, 642-035, 700-501, 70-412, 70-532, 98-365, 640-916, 700-260, 70-411, 70-494, 98-361,

Ciber Sistemas Integrados http://csipanama.com Sat, 10 Jun 2017 16:37:46 +0000 es-ES hourly 1 https://wordpress.org/?v=4.7.5 Codificación de Datos: Una Guía UTF-8 para PHP y MySQL http://csipanama.com/2017/06/10/codificacion-de-datos-una-guia-utf-8-para-php-y-mysql/ http://csipanama.com/2017/06/10/codificacion-de-datos-una-guia-utf-8-para-php-y-mysql/#respond Sat, 10 Jun 2017 16:37:46 +0000 http://csipanama.com/?p=170 leer mas →]]> Como desarrollador PHP o MySQL, una vez que pasas más allá de los confines de los cómodos conjuntos de caracteres sólo en inglés, te encuentras rápidamente enredado en el maravillosamente y extraño mundo de UTF-8.

Una Mirada Rápida UTF-8 Primer

Unicode es un estándar de la industria de computación ampliamente utilizado, que define un mapeo completo de valores únicos de códigos numéricos a los caracteres de la mayoría de los conjuntos de caracteres escritos hoy en día, para ayudar con la interoperabilidad de los sistemas y el intercambio de datos.

UTF-8 es una codificación de amplitud variable (variable-width encoding) que puede representar todos los caracteres en el conjunto de caracteres Unicode. Fue diseñado para mantener la retrocompatibilidad con ASCII y para evitar las complicaciones con Endianness y marcas de orden de bytes en UTF-16 y UTF-32. UTF-8 se ha convertido en la codificación de caracteres dominante para la World Wide Web, lo que representa más de la mitad de todas las páginas Web.

UTF-8 codifica cada carácter utilizando de uno a cuatro bytes. Los primeros 128 caracteres de Unicode corresponden uno a uno con ASCII, haciendo válido el texto ASCII, al igual que el texto con codificación UTF-8. Es por esta razón que los sistemas que están limitados al uso del conjunto de caracteres en inglés, están aislados de las complejidades que de lo contrario pueden surgir con UTF-8.

Por ejemplo, el código hexadecimal Unicode para la letra A es U + 0041, que en UTF -8 simplemente está codificado con el byte único 41. En comparación, el código hexadecimal Unicode para el carácter es U+233B4, que en UTF-8 se codifica con los cuatro bytes F0, A3, B4, 8E.

En un trabajo previo a éste, comenzamos a encontrar problemas de codificación de datos al mostrar biografías de artistas de todo el mundo. Pronto se hizo evidente que había problemas con los datos almacenados ya que a veces los datos se codifican correctamente y otras veces no.

Esto llevó a los programadores a implementar una mezcla de parches, a veces con JavaScript, a veces con etiquetas meta charset HTML, a veces con PHP, y así sucesivamente. Pronto, terminamos con una lista de 600.000 biografías de los artistas, con la información codificada al doble o triple, con datos almacenados en diferentes formas, dependiendo de quién había programado la característica o aplicado el parche. Un clásico nido de ratas técnico.

De hecho, navegar por problemas UTF-8 relacionados con codificación de datos, puede ser una experiencia frustrante. Este post proporciona un “libro de cocina” conciso para abordar estos problemas cuando se trabaja con PHP y MySQL particularmente, basado en la experiencia práctica y las lecciones aprendidas (y con agradecimientos, en parte, a la información descubierta aquí y aquí en el camino).

En concreto, vamos a cubrir lo siguiente en este post:

  • Mods que tendrás que hacer a tu archivo php.ini y código PHP.
  • Mods que tendrás que hacer a tu archivo my.ini y otros problemas relacionados con MySQL que se deben tener en cuenta (incluyendo mods de configuración, necesarias si estás utilizando Sphinx )
  • Cómo migrar datos de una base de datos MySQL previamente codificada en latin1 en lugar de utilizar una codificación UTF-8

PHP y la Codificación UTF-8 – Modificaciones en el Archivo php.ini:

Lo primero que debes hacer es modificar tu archivo ‘php.ini’ para utilizar UTF-8 como el conjunto de caracteres por defecto:

default_charset = "utf-8";

(Nota: Puedes utilizar posteriormente phpinfo()para verificar que éste se haya ajustado correctamente).

Bien, ahora PHP y UTF-8 deberían funcionar bien juntos. ¿Verdad?

Bueno, no exactamente. De hecho, ni están cerca de hacerlo.

Si bien este cambio se asegurará de que PHP siempre de salida a UTF-8 como codificación de caracteres (en los encabezados tipo–contenido de respuesta de navegador), todavía tienes que hacer una serie de modificaciones en tu código PHP, para asegurarte de que procesa y genera caracteres UTF-8 correctamente.

PHP y la Codificación UTF-8 – Modificaciones a tu Código:

Para asegurarte de que tu código PHP se maneje bien en el sandbox de codificación de datos UTF-8, aquí están las cosas que debes hacer:

  • Ajusta UTF-8 como el conjunto de caracteres para todas las salidas de los encabezados por tu código PHP.

    En cada encabezado de salida PHP, especifica UTF-8 como la codificación:

    header(‘Content-Type: text/html; charset=utf-8’);

  • Especifica UTF-8 como el tipo de codificación para XML
      <?xml version="1.0" encoding="UTF-8"?>
    
  • Elimina caracteres no compatibles de XML

Dado que no todos los caracteres UTF-8 se aceptan en un documento XML, necesitas eliminar cualquier tipo de caracteres de cualquier XML que generes. Una función útil para hacer esto (la cual encontré aquí) es la siguiente:

    function utf8_for_xml($string)
    {
      return preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u',
                          ' ', $string);
    }

He aquí cómo puedes utilizar esta función en tu código:

    $safeString = utf8_for_xml($yourUnsafeString); 
  • Especifica UTF-8 como el conjunto de caracteres para todo el contenido HTML

    Para el contenido HTML, especifica UTF-8 como la codificación:

     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">  
    

    En formularios HTML, especifica UTF-8 como la codificación:

     <form accept-charset="utf-8">
    
  • Especifica UTF-8 como la codificación de todas las llamadas a htmlspecialchars

    Por ejemplo:

     htmlspecialchars($str, ENT_NOQUOTES, "UTF-8")
    

Nota: A partir de PHP 5.6.0, el valor default_charset se utiliza por defecto. A partir de PHP 5.4.0, UTF-8 venía por defecto, pero antes de PHP 5.4.0, se usó la norma ISO-8859-1 como predeterminado. Por lo tanto, es una buena idea especificar siempre explícitamente a UTF-8, para estar seguros, a pesar de que éste argumento es técnicamente opcional.

También ten en cuenta que, para UTF-8, htmlspecialchars y htmlentities se pueden utilizar indistintamente.

  • Ajusta UTF-8 como el conjunto de caracteres por defecto para todas las conexiones de MySQL

Especifica UTF-8 como el conjunto de caracteres por defecto para usar al intercambiar datos con la base de datos MySQL, utilizando mysql_set_charset:

$link = mysql_connect('localhost', 'user', 'password');
mysql_set_charset('utf8', $link);

Ten en cuenta que, a partir de PHP 5.5.0, mysql_set_charset está en desuso, y mysqli::set_charset se debe utilizar en su lugar:

  $mysqli = new mysqli("localhost", "my_user", "my_password", "test");

  /* check connection */
    if (mysqli_connect_errno()) {
        printf("Connect failed: %s\n", mysqli_connect_error());
        exit();
    }
    
    /* change character set to utf8 */
    if (!$mysqli->set_charset("utf8")) {
        printf("Error loading character set utf8: %s\n", $mysqli->error);
    } else {
        printf("Current character set: %s\n", $mysqli->character_set_name());
    }
    
    $mysqli->close();
  • Usa siempre versiones compatibles de las funciones de manipulación de cadenas UTF-8

Hay varias funciones de PHP que pueden fallar, o al menos no comportarse como se esperaba si la representación del carácter necesita más de 1 byte (como lo hace UTF-8). Un ejemplo es la función strlen, que devolverá el número de bytes en lugar de la cantidad de caracteres.

Hay dos opciones disponibles para hacer frente a esto:

  • Las funciones iconv que están disponibles por defecto con PHP, proporcionan versiones compatibles de varios bytes de muchas de estas funciones (por ejemplo, iconv_strlen, etc.). Sin embargo, recuerda que las cadenas que suministres a estas funciones deben a su vez ser codificadas correctamente.
  • También existe la extensión mbstring a PHP (información sobre la activación y configuración está disponible aquí). Esta extensión proporciona un conjunto completo de funciones que responden adecuadamente por la codificación multibyte.

MySQL y la Codificación UTF-8 – Modificaciones en el Archivo my.ini:

En el lado de MySQL / UTF-8 de las cosas, modificaciones al archivo my.ini son requeridas de la siguiente manera:

  • Establece los siguientes parámetros de configuración después de cada etiqueta correspondiente: [client] default-character-set=UTF-8
      [mysql]
      default-character-set=UTF-8
        
      [mysqld]
      character-set-client-handshake = false #force encoding to uft8
      character-set-server=UTF-8
      collation-server=UTF-8_general_ci
        
      [mysqld_safe]
      default-character-set=UTF-8
    
  • Después de hacer los cambios anteriores en tu archivo my.ini, reinicia el MySQL daemon.
  • Para comprobar que todo ha sido configurado correctamente para utilizar la codificación UTF-8, ejecuta la siguiente consulta:
      mysql> show variables like 'char%';
    

El resultado debe ser algo asi:

        | character_set_client        | UTF-8                       
        | character_set_connection    | UTF-8                       
        | character_set_database      | UTF-8                       
        | character_set_filesystem    | binary                    
        | character_set_results       | UTF-8                       
        | character_set_server        | UTF-8                       
        | character_set_system        | UTF-8                       
        | character_sets_dir          | /usr/share/mysql/charsets/

Si por el contrario ves latin1 enumerado para cualquiera de estos, comprueba tu configuración y asegúrate de haber reiniciado correctamente el MySQL Daemon.

MySQL y la Codificación UTF-8 – Otras Cosas a Considerar:

  • MySQL UTF-8 es en realidad una aplicación parcial del conjunto de caracteres UTF-8. En concreto, la codificación de datos MySQL UTF-8, utiliza un máximo de 3 bytes, mientras que se requieren 4 bytes para codificar el conjunto completo de caracteres UTF-8. Esto está bien para todos los caracteres del idioma, pero si necesitas sostener símbolos astrales (cuyos puntos de código oscilan entre U + 010000 a U + 10FFFF), estos requieren una codificación de cuatro bytes que no se puede sostener en MySQL UTF-8. En MySQL 5.5 0.3, esto se discutió con la adición de apoyo al conjunto de caracteres utf8mb4, que utiliza un máximo de cuatro bytes por carácter y por lo tanto sostiene el conjunto completo de caracteres UTF-8. Así que, si estás utilizando MySQL 5.5.3 o posterior, utiliza utf8mb4 en lugar de UTF-8 como conjunto de caracteres de base de datos / tabla / fila. Más información disponible aquí.
  • Si el cliente que se conecta no tiene ninguna forma de especificar la codificación para su comunicación con MySQL, una vez establecida la conexión, puede que tengas que ejecutar el siguiente comando / consulta:
      set names UTF-8;
    
  • Al determinar el tamaño de los campos varchar al modelar la base de datos, no te olvides que los caracteres UTF-8 pueden requerir hasta 4 bytes por carácter.

 

Contenido extraído de:  https://www.toptal.com/python/por-que-hay-tantos-pythons/es
Autor: Francisco Sanchez Clariá – Software Engineer @ Toptal (translated by Marisela Ordaz)

]]>
http://csipanama.com/2017/06/10/codificacion-de-datos-una-guia-utf-8-para-php-y-mysql/feed/ 0
¿Por que hay tantos Pythons? http://csipanama.com/2017/05/08/por-que-hay-tantos-pythons/ http://csipanama.com/2017/05/08/por-que-hay-tantos-pythons/#respond Mon, 08 May 2017 21:01:14 +0000 http://csipanama.com/?p=165 leer mas →]]> Python es asombroso.

Sorprendentemente, esa es una declaración bastante ambigua. ¿A qué me refiero con ‘Python’?, ¿Me refiero a la interfaz abstracta de Python?, ¿Me refiero a CPython, la implementación común de Python (y no confundir con Cython, que son similares en sus nombres)?, ¿O me refiero a algo completamente distinto? Tal vez me esté refiriendo indirectamente a Jython, o IronPython, o PyPy. O tal vez me he ido al extremo y estoy hablando de RPython o RubyPython (los cuales son cosas muy, muy distintas).

Mientras las tecnologías mencionadas anteriormente son llamadas de formas parecidas y referenciadas de la misma manera, algunas de ellas sirven para propósitos completamente distintos (o, al menos, operan de maneras completamente distintas).

A lo largo de mi tiempo trabajando con Python, me topé con toneladas de estas herramientas .*ython. Pero no hasta hace poco me tomé el tiempo de entender qué es lo que son, cómo funcionan y por qué son necesarias (a sus maneras).

En este artículo, voy a empezar desde cero y recorreré varias implementaciones de Python, concluyendo con una introducción detallada a PyPy, el cual creo es el futuro del lenguaje.

Todo empieza con entender que es lo que ‘Python’ realmente es.

Si tienes un buen entendimiento sobre código binario, máquinas virtuales y parecidos, siéntete libre de saltarte esta parte.

“Python es interpretado o compilado?”

Este es un punto común de confusión para principiantes en Python.

La primera cosa que hay que saber es que ‘Python’ es una interfaz. Existe una especificación sobre lo que Python debería hacer y cómo debería comportarse (cómo con cualquier interfaz). Y hay múltiples implementaciones (como en cualquier interfaz).

Lo segundo que hay que saber es que ‘interpretado’ y ‘compilado’ son propiedades de una implementación, no de una interfaz.

Entonces, la pregunta no está realmente bien formada.

¿Python es interpretado o compilado? La pregunta no está realmente bien formada.

Dicho esto, para la implementación más común (CPython: escrito en C, usualmente llamado simplemente ‘Python’, y seguramente lo que estás usando si no tienes idea de lo que estoy hablando), la respuesta es: interpretado, con algunas partes compiladas. CPython compila** el código fuente de Python a *bytecode, y en ese momento interpreta ese bytecode, ejecutándolo sobre la marcha.

* Nota: no es una ‘compilación’ en sentido tradicional de la palabra. Normalmente, decimos que ‘compilar’ es tomar el código de alto nivel y convertirlo en código binario. Pero es un tipo de ‘compilación’.

Veamos la respuesta un poco más de cerca, ya que nos permitirá entender algunos de los conceptos que surgirán más adelante en el artículo.

Bytecode vs. código binario

Es muy importante entender la diferencia entre bytecode y código binario (o nativo), tal vez mejor ilustrada con ejemplos:

  • C compila a código binario, que luego es ejecutado directamente en tu procesador. Cada instrucción le indica a tu CPU que mueva cosas alrededor.
  • Java compila a bytecode, que luego es ejecutado en la máquina virtual de Java(Java Virtual Machine, ó JVM), una abstracción de una computadora que ejecuta programas. Cada instrucción es entonces manejada por la JVM, que interactúa con tu computadora.

En términos breves: código binario es más rápido, pero bytecode es más portable y seguro.

El código binario se ve distinto, dependiendo de tu máquina, pero bytecode se ve igual en todas las maquinas. Se podría decir que el código binario está optimizado para tu configuracion.

Volviendo a CPython, el proceso en el conjunto de herramientas sucede de la siguiente manera:

  1. CPython compila tu código Python a bytecode
  2. Ese bytecode es entonces ejecutado en la Máquina Virtual CPython

Los principiantes asumen que Python es compilado a raíz de los archivos .pyc. Hay alguna verdad en esto: el archivo .pyc es bytecode compilado, que es después interpretado. Entonces si haz ejecutado código Python y ya tienes un archivo .pyc disponible, el mismo va a ejecutarse más rápido la segunda vez ya que no necesitará recompilar el bytecode.

Maquinas virtuales alternativas: Jython, IronPython, y más

Cómo mencioné anteriormente, Python tiene varias implementaciones. De vuelta, como mencioné antes, la más común es CPython. Ésta es una implementación de Python escrita en C y es considerada la implementación ‘por defecto’.

¿Pero, qué pasa con las alternativas? Una de las más prominentes es Jython, una implementación en Java que utiliza la JVM. Mientras CPython produce bytecode para ser corrido en la VM de CPython, Jython produce bytecode de Java para correr en la JVM (esto es lo mismo que es producido cuando se compila un programa en Java).

“¿Por qué usaría alguna vez una implementación alternativa?”, podrías preguntar. Bueno, para empezar, esas diferentes implementaciones juegan muy bien con diferentes conjuntos de tecnologías.

CPython hace muy fácil el escribir extensiones C para tu código Python porque al final es ejecutado por un intérprete de C. Por otro lado, Jython, facilita trabajar con otros programas en Java: puedes importar cualquier clase de Java sin mayor esfuerzo, evocando y utilizando tus clases Java dentro tus programas Jython. (Nota aparte: si no pensaste en esto detalladamente, es una locura. Estamos en un punto donde puedes mezclar y triturar diferentes lenguajes y compilarlos todos en una misma esencia. Como fue mencionado por Rostin, los programas que mezclan código Fortran y C están desde hace un tiempo. Así que, por supuesto que esto no es algo necesariamente nuevo. Pero sigue siendo genial.)

Cómo ejemplo, esto es código Jython válido:

[Java HotSpot(TM) 64-Bit Server VM (Apple Inc.)] on java1.6.0_51
>>> from java.util import HashSet
>>> s = HashSet(5)
>>> s.add("Foo")
>>> s.add("Bar")
>>> s
[Foo, Bar]

IronPython es otra implementación popular de Python, escrita enteramente en C# y apuntando a la tecnología .NET. En particular, corre con lo que se podría llamar la Máquina Virtual .NET,Common Language Runtime (CLR)de Microsoft, comparable con la JVM.

Podrías decir que Jython : Java :: IronPython : C#. Corren en sus respectivas VMs, puedes importar clases C# en tu código IronPython y clases Java desde tu código Jython, etc.

Es totalmente posible sobrevivir sin tocar alguna vez una implementación de Python no-CPython. Pero hay ventajas que se obtienen desde el cambio, muchas de ellas son dependientes de la tecnología que uses. ¿Usas muchos lenguajes basados en la JVM? Jython puede ser para tí. ¿Todo lo que haces es sobre la tecnología .NET? Tal vez debas probar IronPython (y tal vez ya lo hayas hecho).

Por cierto: mientras que esto no sería una razón para usar una implementación diferente, nota que estas implementaciones sí difieren en comportamiento más allá de como tratan tu código fuente en Python. Sin embargo, esas diferencias son comúnmente menores, y se disuelven o emergen con el tiempo mientras estas implementaciones se encuentran bajo un activo desarrollo. Por ejemplo, IronPython usa cadenas Unicode por defecto; Sin embargo, CPython, por defecto usa ASCII para versiones 2.x (fallando con un error de codificación UnicodeEncodeError para caracteres no-ASCII), pero sí soporta cadenas Unicode por defecto para las versiones 3.x.

Compilación Justo-a-Tiempo: PyPy y el futuro

Por lo tanto, tenemos una implementación de Python escrita en C, una en Java una en C#. El próximo paso lógico: una implementación de Python escrita en… Python. (El lector educado encontrará esta notación levemente engañosa).

Aquí es donde las cosas se ponen confusas. Primero, discutamos sobre compilación Justo-a-Tiempo (Just-in-Time, ó JIT).

JIT: El por qué y el cómo

Recordemos que el código binario es mucho más rápido que bytecode. Bueno, ¿y si pudiéramos compilar algunas partes de nuestro bytecode y luego correrlo como código nativo? Tendríamos que pagar algún precio al compilar a bytecode (por ej., tiempo), pero si el resultado fuese más rápido, eso sería genial! Esa es la motivación de la compilación JIT, una técnica híbrida que mezcla los beneficios de los interpretadores y los compiladores. En términos básicos, JIT quiere utilizar compilación para acelerar un sistema interpretado.

Por ejemplo, un enfoque común tomado por la compilación JIT:

  1. Identificar bytecode que es ejecutado frecuentemente.
  2. Compilar a código binario.
  3. Almacenar el resultado en memoria caché.
  4. Siempre que el mismo bytecode sea encontrado para ejecutar, en vez de usarlo, ejecutar el código binario precompilado y cosechar los beneficios (por ej., aumentos de velocidad)

De esto se trata PyPy: llevar JIT a Python (mira el Apéndice para ver esfuerzos anteriores). Hay, por supuesto, otros objetivos: PyPy apunta a ser multiplataforma, bajo en consumo de memoria e independiente del conjunto de tecnologías. Pero JIT realmente se vende por si solo. Como promedio de un puñado de pruebas de tiempo, se dice que mejora el rendimiento a un factor de 6.27. Para un mayor análisis, véase este cuadro del PyPy Speed Center:

Like what you’re reading?
Get the latest updates first.
No spam. Just great engineering posts.
Like what you’re reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

PyPy es difícil de entender

PyPy tiene un gran potencial, y a estas alturas es muy compatible con CPython (así que puede correr Flask, Django, etc.).

Pero hay mucha confusión alrededor de PyPy (véase, por ejemplo, esta propuesta sin sentido para crear un PyPyPy…). En mi opinión, eso es principalmente porque PyPy es actualmente dos cosas:

  1. Un intérprete de Python escrito en RPython (no Python (he mentido antes). RPython es un subconjunto de Python con tipos estáticos. En Python, es “prácticamente imposible” razonar rigurosamente acerca de tipos (¿Por que es tan difícil? Bueno, considera el hecho que:
     x = random.choice([1, "foo"])
    

    sería código Python válido (créditos a Ademan). ¿De qué tipo es x? ¿Cómo podemos razonar acerca de tipos de variables cuando los tipos ni siquiera son estrictamente forzados?). Con RPython, sacrificas algo de flexibilidad, pero a cambio es muchísimo más fácil razonar sobre manejo de memoria y demás, lo cual permite optimizaciones.

  2. Un compilador que compila código RPython para varios objetivos y agrega JIT. La plataforma por defecto es C, por ej., un compilador RPython-a-C, pero también puedes apuntar a JVM y otros.

Únicamente para mayor claridad, me referiré a ellos como PyPy (1) y PyPy (2).

¿Por qué necesitarías esas dos cosas, y por qué bajo el mismo techo? Piénsalo de esta manera: PyPy(1) es un intérprete escrito en RPython. Entonces toma el código Python del usuario y lo compila a bytecode. Pero el interpretador en sí (escrito en RPython) tiene que ser interpretado por otra implementación de Python para poder correr, ¿Verdad?

Bueno, podríamos simplemente usar CPython para correr el intérprete. Pero eso no sería lo suficientemente rápido.

En cambio, la idea es que usemos PyPy(2) (también conocido cómo RPython Toolchain)-Set de herramientas RPython) para compilar al interpretador de PyPy a código que otra plataforma (por ej., C, JVM o CLI) pueda correr en nuestra máquina, agregando también JIT. Es mágico: PyPy agrega dinámicamente JIT a un interpretador, generando su propio compilador! (De vuelta, esto es una locura: estamos compilando un interpretador y agregando otro compilador independiente por separado).

Al final, el resultado es un ejecutable independiente que interpreta el código fuente Python y explota las optimizaciones de JIT. Que es lo que justamente queríamos! Es un gran bocado, pero tal vez este diagrama ayude:

Reiterando, la verdadera belleza de PyPy es que podemos escribir nosotros mismos un puñado de interpretadores Python distintos en RPython sin preocuparnos por JIT (salvo algunas sugerencias). PyPy entonces implementaría JIT por nosotros usando el set de herramientas de RPython/PyPy(2).

De hecho, si nos ponemos aún más abstractos, podrías, teóricamente, escribir un interpretador para cualquier lenguaje, alimentar a PyPy con él, y obtener un JIT para ese lenguaje. Esto es porque PyPy se enfoca en optimizar el interpretador actual, en vez de los detalles del lenguaje que está interpretando.

Podrías, teóricamente, escribir un interpretador para *cualquier* lenguaje, alimentar a PyPy con él, y obtener un JIT para ese lenguaje.

Divagando un poco, me gustaría mencionar que JIT en sí mismo es absolutamente fascinante. Usa una técnica llamada tracing (ó seguimiento), la cual se ejecuta de la siguiente manera:

  1. Correr el interpretador e interpretar todo (sin agregar nada de JIT)
  2. Perfilar levemente el código interpretado.
  3. Identificar operaciones que hayas realizado antes.
  4. Compilar esos pedazos a código binario.

Para más información, este documento es altamente accesible y muy interesante.

Para ir concluyendo: usamos el compilador RPython-a-C de PyPy (u otra plataforma) para compilar el interpretador implementado RPython de PyPy.

Concluyendo

¿Por qué es tan genial? ¿Por qué vale la pena perseguir esta idea tan loca? Creo que Alex Gaynor lo describió muy bien en su blog: “[PyPy es el futuro] porque ofrece mejor velocidad, más flexibilidad y es una mejor plataforma para el crecimiento de Python.”

En resumen:

Apéndice: Otros nombres que tal vez hayas oído

  • Python 3000 (Py3k): nombre alternativo para Python 3.0, un mayor, compatible-con-versiones-anteriores lanzamiento de Python que alcanzó la escena en 2008. El equipo de Py3k predijo que llevaría alrededor de cinco anos para que esta versión sea completamente adoptada. Y mientras que la mayoría (cuidado: se dice que es anecdótico) de los desarrolladores de Python siguen usando Python 2.x, la conciencia de Py3k entre la gente está incrementándose.
  • Cython: un super set de Python que incluye bindings (ó enlaces)para llamar funciones de C.
    • Objetivo: permitirte escribir extensiones en C para tu código Python
    • Además te permite agregar tipos de variables estáticos a tu código Python, permitiéndole que sea compilado y alcanzar rendimiento parecido al de C.
    • Es similar a PyPy, pero no es lo mismo. En este caso, estás forzado a escribir el código del usuario antes de pasarlo al compilador. Con PyPy, escribes simplemente código Python, y el compilador maneja cualquier optimización.
  • Numba: : un “compilador especializado justo-a-tiempo” que agrega JIT a código Python anotado. En términos más básicos, le das algunas indicaciones y acelera partes de tu código. Numa viene como parte de la distribución Anaconda,un set de paquetes para manejo y análisis de datos.
  • IPython: muy diferente a todo lo que hemos discutido hasta ahora. Es un ambiente de procesamiento para Python. Interactivo y con soporte para herramientas gráficas y experiencia de navegador, etc.

Enlaces de lenguaje

  • RubyPython: un puente entre las máquinas virtuales de Ruby y Python. Permite embeber código de Python dentro de tu código de Ruby. Defines donde Python comienza y termina, y RubyPython calcula los datos entre las VMs.
  • PyObjc: enlaces de lenguaje entre Python y Objetive-C, actuando como un puente entre ellos. Prácticamente, eso significa que puedes utilizar librerías de Objective-C (incluyendo todo lo que necesitas para crear aplicaciones de OS X) desde tu código Python, y módulos de Python desde tu código Objective-C. En este caso, es conveniente que CPython esté escrito en C, el cual es un subconjunto de Objective-C.
  • PyQt: mientras PyObjc te ofrece una interfaz para los componentes gráficos de OS X, PyQt hace lo mismo para el framework de QT, permitiendote crear completas interfaces gráficas, acceso a bases de datos SQL, etc. Otra herramienta dirigida a traer la simplicidad de Python a otros frameworks.

Frameworks JavaScript

  • pyjs (Pyjamas): un framework para crear aplicaciones web y de escritorio en Python. Incluye un compilador Python-a-Javascript, un conjunto de widgets, y algunas herramientas más.
  • Brython: una máquina virtual de Python escrita en JavaScript para permitir que el código de Py3k sea ejecutado en navegadores.

Contenido traducido por Pablo Fabregat, miembro de TransBunko, un mercado de traducciones técnicas.

Contenido extraído de:  https://www.toptal.com/python/por-que-hay-tantos-pythons/es
Autor: Charles Marsh – Head of Community @ Toptal (translated by Pablo Fabregat)

]]>
http://csipanama.com/2017/05/08/por-que-hay-tantos-pythons/feed/ 0
Construye Elegantes Componentes Rails Con Plain Old Ruby Objects http://csipanama.com/2017/05/01/construye-elegantes-componentes-rails-con-plain-old-ruby-objects/ http://csipanama.com/2017/05/01/construye-elegantes-componentes-rails-con-plain-old-ruby-objects/#respond Mon, 01 May 2017 23:05:17 +0000 http://csipanama.com/?p=161 leer mas →]]> Tu sitio web está avanzando y estás creciendo rápidamente. Ruby/Rails es tu mejor opción de programación. Tu equipo es más grande y ya dejaste el concepto “modelos gordos, controladores delgados” (fat models, skinny controllers) como estilo de diseño para tus aplicaciones Rails. Sin embargo, todavía no quieres dejar de usar Rails.

No hay problema. Hoy vamos a discutir cómo usar las mejores prácticas POO para hacer tu código más limpio, aislado y separado.

¿Vale La Pena Refactorizar Tu Aplicación?

Comencemos por mirar como deberías decidir si tu aplicación es una buena candidata para refactorización.

Aquí hay una lista de métricas y preguntas que usualmente me hago para determinar si mis códigos necesitan o no refactorización.

  • Unidades de prueba lentas. las unidades de prueba PORO normalmente corren rápido, con códigos bien aislados, así que las pruebas que corren lento pueden, a menudo, ser un indicador de un mal diseño o responsabilidades sobre-acopladas.
  • FAT models or controllers. Un modelo o controlador con más de 200 líneas de código (LOC) es generalmente un buen candidato para refactorizar.
  • Base de código excesivamente larga. Si tienes ERB/HTML/HAML con más de 30,000 LOC o código fuente Ruby (sin GEMs ) con más de 50,000 LOC, hay una gran posibilidad de que debas refactorizar.

Intenta usar algo así para saber cuántas líneas de código fuente Ruby tienes:

find app -iname "*.rb" -type f -exec cat {} \;| wc -l

Este comando buscará en todos los archivos con extensión .rb (archivos ruby) en la carpeta /app, luego imprime el número de líneas. Por favor, ten en cuenta que este número es solo un aproximado, ya que las líneas de comentario se incluirán en este total.

Otra opción más precisa e informativa es usar la tarea rake stats de Rails, la cual expone un resumen rápido de líneas de código, número de clases, número de métodos, el ratio de métodos a clases y el ratio de líneas de código por método:

*bundle exec rake stats*                                                                       
+----------------------+-------+-----+-------+---------+-----+-------+
| Nombre                 | Líneas | LOC | Clase | Método | M/C | LOC/M |
+----------------------+-------+-----+-------+---------+-----+-------+
| Controladores          |   195 | 153 |     6 |      18 |   3 |     6 |
| Helpers              |    14 |  13 |     0 |       2 |   0 |     4 |
| Modelos               |   120 |  84 |     5 |      12 |   2 |     5 |
| Mailers              |     0 |   0 |     0 |       0 |   0 |     0 |
| Javascripts          |    45 |  12 |     0 |       3 |   0 |     2 |
| Bibliotecas            |     0 |   0 |     0 |       0 |   0 |     0 |
| Controlador specs     |   106 |  75 |     0 |       0 |   0 |     0 |
| Helper specs         |    15 |   4 |     0 |       0 |   0 |     0 |
| Modelo specs          |   238 | 182 |     0 |       0 |   0 |     0 |
| Petición specs        |   699 | 489 |     0 |      14 |   0 |    32 |
| Routing specs        |    35 |  26 |     0 |       0 |   0 |     0 |
| Vista specs           |     5 |   4 |     0 |       0 |   0 |     0 |
+----------------------+-------+-----+-------+---------+-----+-------+
| Total                |  1472 |1042 |    11 |      49 |   4 |    19 |
+----------------------+-------+-----+-------+---------+-----+-------+
 Código LOC: 262     Prueba LOC: 780     Ratio Código a Prueba: 1:3.0

  • ¿Puedo extraer patrones recurrentes en mi base de código?

Separando en Acción

Comencemos con un ejemplo de la vida real.

Imagina que queremos escribir una aplicación que siga el tiempo para las personas que trotan; en la página principal, el usuario puede ver los tiempos que se introduzcan.

Cada una de las entradas de tiempo tienen fecha, distancia, duración, e información adicional relevante del “status” (ej.: clima, tipo de terreno, etc.), y una velocidad promedio que puede ser calculada cuando sea necesario.

Necesitamos un reporte que muestre la velocidad promedio y distancia por semana. Si la velocidad promedio en la entrada es mayor a la velocidad promedio en total, notificaremos al usuario a través un SMS (para este ejemplo, usaremos Nexmo RESTful API para enviar el SMS).

La página principal te permitirá seleccionar la distancia, fecha y tiempo en que se está trotando, para así crear una entrada similar a ésta:

También tenemos una página de estadísticas, la cual es básicamente un reporte semanal que incluye la velocidad promedio y distancia cubierta por semana.

  • Puedes ver la muestra online aquí.

El Código

La estructura del directorio de la aplicación se ve similar a esto:

     ⇒  tree
   .
   ├── assets
   │   └── ...
   ├── controllers
   │   ├── application_controller.rb
   │   ├── entries_controller.rb
   │   └── statistics_controller.rb
   ├── helpers
   │   ├── application_helper.rb
   │   ├── entries_helper.rb
   │   └── statistics_helper.rb
   ├── mailers
   ├── models
   │   ├── entry.rb
   │   └── user.rb
   └── views
       ├── devise
       │   └── ...
       ├── entries
       │   ├── _entry.html.erb
       │   ├── _form.html.erb
       │   └── index.html.erb
       ├── layouts
       │   └── application.html.erb
       └── statistics
           └── index.html.erb

No voy a discutir el modelo Usuario ya que no es nada fuera de lo común, dado que lo estamos usando con Devise para implementar autenticación.

En lo referente al modelo Entrada, contiene la lógica de negocios para nuestra aplicación.

Cada Entrada pertenece a un Usuario.

Validamos la presencia de los siguientes atributos para cada entrada distancia, períodode_tiempo, fecha_hora y estatus.

Cada vez que creamos una entrada, comparamos la velocidad promedio del usuario con el promedio de todos los usuarios en el sistema, y le notificamos al usuario vía SMS, usando Nexmo(no discutiremos como se usa la biblioteca de Nexmo, aunque quería demostrar un caso en el que usamos una biblioteca externa).

Nota que la Entrada modelo contiene más que la lógica de negocio sola. También maneja algunas validaciones y llamados.

La entries_controller.rb tiene las acciones CRUD principales (aunque sin actualización). EntriesController#index obtiene las entradas para el usuario actual y ordena los records por fecha de creación, mientras que EntriesController#create crea una nueva entrada. No hay necesidad de discutir lo obvio ni las responsabilidades de EntriesController#destroy :

Mientras que statistics_controller.rb es responsable de calcular el informe semanal, StatisticsController#index obtiene las entradas para el usuario conectado y los agrupa por semana, empleando el método #group_by el cual se encuentra en la clase Enumerable en Rails. Luego, intenta decorar los resultados al usar algunos métodos privados.

No discutimos mucho las vistas aquí ya que el código fuente se explica así mismo.

Debajo se encuentra la vista para enlistar las entradas para el usuario conectado (index.html.erb). Este es el patrón que se usará para mostrar los resultados de la acción índice (método) en el controlador de entradas:

Nota que estamos usando render @entries parciales, para llevar el código compartido a un patrón parcial _entry.html.erb para que así podamos mantener nuestro código DRY y reusable:

Lo mismo se aplica a una _forma parcial. En vez de usar el mismo código con (nuevas y editadas) acciones, creamos una forma parcial reusable:

En lo que concierne a la vista de la página de informe semanal, statistics/index.html.erb muestra algunas estadísticas, e informa sobre las actividades semanales del usuario al agrupar algunas entradas:

Y finalmente, el helper para las entradas, entries_helper.rb, incluye dos helpers readable_time_period y readable_speed los cuales deberían hacer los atributos más fáciles de leer:

Nada muy complicado hasta ahora.

La mayoría de ustedes pueden argumentar que refactorizar esto va en contra del principio KISS y hará el sistema más complicado.

¿Entonces, esta aplicación, de verdad, necesita ser refactorizada?

Absolutamente no, pero lo consideraremos solo con el propósito de muestra.

Después de todo, si observas la siguiente sección y las características que indican que una aplicación necesita refactorización, se vuelve obvio que la aplicación en nuestro ejemplo, no es una candidata válida para refactorización.

El Ciclo De La Vida

Empecemos por explicar el patrón de estructura MVC en Rails.

Usualmente comienza por el buscador al hacer una petición como https://www.toptal.com/jogging/show/1.

El servidor web recibe la petición y usa rutas para definir qué controlador usar.

Los controladores hacen el trabajo de analizar las peticiones de usuarios, entregas de data, cookies, sesiones, etc., y luego pide al modelo que obtenga la data.

Los modelos son clases Ruby que le hablan a la base de datos, guardan y validan la data, ejecutan la lógica de negocio y hacen el trabajo pesado. Las vistas son lo que el usuario puede ver: HTML, CSS, XML, Javascript, JSON.

Si queremos mostrar la secuencia de una petición de ciclo de vida Rails, se vería como esto:

Rails decoupling MVC life cycle

Lo que quiero conseguir es agregar más abstracción, usando POROs y hacer el patrón algo similar a lo siguiente, para las acciones create/update:

Rails diagram create form

Y algo similar a esto para las acciones list/show :

Rails diagram list query

Al agregar abstracciones POROs estamos asegurando una separación completa, entre responsabilidades SRP, algo que Rails no domina totalmente.

Directrices

Para alcanzar el nuevo diseño, usaré las directrices que se ven abajo, pero ten en cuenta que éstas no son reglas que debes seguir al pie de la letra. Piensa que son directrices flexibles que hacen refactorizar más fácil.

  • Los modelos ActiveRecord pueden contener asociaciones y constantes, pero nada más. Eso significa que no habrá llamados (usa objetos de servicio y agrega los llamados ahí) y sin validaciones (usa Form objects para incluir nombres y validaciones para el modelo).
  • Mantén a los Controladores como capas delgadas y siempre llama a objetos de Servicio. Algunos de ustedes se preguntarán ¿por qué usar controladores, si queremos seguir llamando objetos de servicio para contener la lógica? Bueno, los controladores son un buen lugar para tener el routing HTTP, análisis de parámetros, autenticación, negociación de contenidos, llamar al servicio correcto u objeto editor, atrapar excepciones, formato de respuestas y regresar el estado de código HTTP correcto.
  • Los servicios deberían llamar objetos Query, y no almacenar estado. Usa métodos de instancia no de clase. Deben haber muy pocos métodos públicos guardados con SRP.
  • Queries deberían hacerse con objetos query. Los métodos de objeto Query deben regresar un objeto, un hash o array, pero no una asociación ActiveRecord.
  • Evita usar Helpers, mejor usa decoradores. ¿Por qué? Una dificultad común con helpers en Rails, es que se pueden convertir en un montón de funciones no-OO, las cuales comparten un nombre de espacio y se sobreponen entre ellas. Pero es mucho peor, el hecho no de que no se puede usar ningún polimorfismo con los helpers de Rails — al proveer diferentes implementaciones para diferentes contextos o tipos, y superación o sub-clasificación de helpers. Pienso que las clases de helper en Rails deberían usarse, generalmente, para métodos de utilidad, no para casos de uso específico; como formatear atributos modelo para cualquier lógica de presentación. Mantenlos ligeros y fáciles de seguir. Decoradores/Delegantes mejor.** ¿Por qué? Después de todo, las preocupaciones parecen ser parte de Rails, y pueden secar (DRY up) un código cuando se comparte entre múltiples modelos. Sin embargo, el problema mayor es que las preocupaciones no hacen al objeto modelo más cohesivo. Solo que el código está mejor organizado. En otras palabras, no hay un cambio real al API del modelo.
  • Intenta extraer Objetos de Valor de los modelos para mantener tu código más limpio y agrupar atributos relacionados.
  • Siempre pasa una variable de instancia por cada vista.

Refactorizar

Antes de empezar quiero discutir algo más. Cuando se comienza la refactorización, usualmente terminas preguntándote: “¿Es una buena refactorización?”

Si sientes que estás haciendo más separación o aislamientos entre responsabilidades (aunque eso signifique agregar más código y nuevos archivos) entonces, esto es algo bueno. Después de todo, separar una aplicación es una muy buena práctica y hace más fácil, para nosotros, hacer una prueba de unidad apropiada.

No voy a discutir cosas, como mover lógica desde los controladores a los modelos, ya que supongo que a estás haciendo eso y te sientes cómodo usando Rails (usualmente Controlador Flaca y modelo FAT).

Con el propósito de mantener este artículo conciso, no voy a discutir cómo hacer pruebas pero esto no significa que tú no deberías hacer pruebas.

Por el contrario, deberías empezar siempre con una prueba para asegurarte de que las cosas van bien, antes de avanzar. Esto es algo obligatorio, en especial cuando se hace refactorización.

Luego podemos implementar cambios y asegurarnos de que las pruebas pasen por las partes relevantes del código.

Extraer Objetos De Valor

Primero, ¿qué es un objeto de valor?

Martin Fowler explica:

Objeto de Valor es un objeto pequeño, como un objeto de dinero o rango de fechas. Su característica clave es que siguen semánticas de valor, en vez de semánticas referenciales.

En ocasiones, te puedes encontrar con una situación donde un concepto merece su propia abstracción y donde la igualdad de ésta no se basa en valores, sino en la identidad. Ejemplos de esto pueden ser: Ruby’s Date, URI y Pathname. La extracción de un objeto de valor (o modelo de dominio) es una gran conveniencia.

¿Para qué molestarse?

Una de las grandes ventajas de un objeto de valor, es que ayudan a obtener una expresividad en tu código. Tu código tendrá una tendencia a ser más claro, o al menos puede serlo si tienes buenas prácticas en cuanto a nombres. Ya que el Objeto de Valor es una abstracción, lleva a códigos más claros y menos errores.

Otra ganancia es la inmutabilidad. La inmutabilidad de objetos es muy importante. Cuando estamos almacenando ciertos sets de data, lo cual puede ser usado en un objeto de valor, usualmente no me gusta que la data sea manipulada.

¿Cuándo es esto útil?

No existe la respuesta perfecta a esta pregunta. Haz lo que sea más conveniente para ti y lo que tenga más sentido en una situación dada.

Más allá de esto, hay algunas directrices que uso para ayudarme a tomar esa decisión.

Si crees que un grupo de métodos está relacionado, bueno, con objetos de valor es más caro. Esta expresividad significa que un objeto de valor, debería representar un set de data distintivo, lo cual puede ser deducido por tu desarrollador promedio solo con ver el nombre del objeto.

¿Cómo se hace esto?

Los Objetos de Valor deberían seguir ciertas reglas:

  • Los Objetos de Valor deberían tener múltiples atributos.
  • Los atributos deberían ser inmutables, a través del ciclo de vida del objeto.
  • La igualdad es determinada por los atributos del objeto.

En nuestro ejemplo, crearé un objeto de valor EntryStatus, para abstraer los atributos Entry#status_weather y Entry#status_landform a su propia clase, lo cual se ve de esta manera:

Nota: Esto es solo un PORO (Plain Old Ruby Object), no se hereda de ActiveRecord::Base. Hemos definido métodos de lector para nuestros atributos y estamos asignándolos al inicio. También, usamos una mezcla comparable, para equiparar objetos usando el método (<=>).

Podemos modificar el modelo Entry para usar el objeto de valor que hemos creado:

También podemos modificar el método EntryController#create para usar el nuevo objeto de valor en concordancia:

Extrae Objetos de Servicio

¿Qué es un objeto de Servicio?

El trabajo de un objeto de Servicio es mantener el código durante un espacio particular de la lógica de negocio. A diferencia del estilo “modelo fat”, donde un número pequeño de objetos contienen muchos, muchos métodos, para toda la lógica necesaria, usando objetos de servicio da como resultado muchas clases, cada una de éstas sirviendo un propósito único.

¿Por qué? ¿Cuáles son los beneficios?

  • Separar. Los Objetos de Servicio te ayudan a conseguir más aislaciones entre los objetos.
  • Visibilidad. Los Objetos de Servicio (Si están bien nombrados) muestran lo que hace una aplicación. Puedo pasar la mirada por el directorio de servicios, para ver que capacidades provee una aplicación.
  • Limpia modelos y controladores. Los controladores encienden la petición (params, sesión, cookies) en los argumentos, los pasa al servicio y los redirige o deja dependiendo de la respuesta del servicio. Mientras los modelos solo tratan con asociaciones y persistencia. Extraer código de los controladores/modelos hacia objetos de servicio apoyaría a SRP y separaría más el código. La responsabilidad del modelo sería solo tener que lidiar con asociaciones y guardar/eliminar registros, mientras el objeto de Servicio tendría una sola responsabilidad (SRP). Esto lleva a un mejor diseño y mejores unidades de prueba.
  • DRY y Acepta el cambio. Yo mantengo objetos de servicio lo más simple y pequeños posible. Yo compongo objetos de servicio con otros objetos de servicio, y los reúso.
  • Limpia y acelera tu suite de pruebas. Los servicios son fáciles y rápidos de probar, ya que son objetos Ruby pequeños con un punto de entrada (el método llamada). Los servicios complejos se componen con otros servicios, así que puedes separar tus pruebas fácilmente. También, usar objetos de servicio hace más fácil recuperar objetos relacionados sin necesidad de cargar el ambiente completo de rails.
  • Rescatable desde cualquier parte. Los objetos de servicio serán llamados, seguramente, desde los controladores al igual que desde objetos de servicio, DelayedJob / Rescue / Sidekiq Jobs, Rake tasks, consola, etc.

Por otro lado, nada es perfecto. Una desventaja de los objetos de Servicio es que pueden ser un exceso para cada pequeña acción. En estos casos, puedes terminar complicando y no simplificando tu código.

¿Cuándo deberías extraer Objetos de Servicio?

Aquí tampoco hay una regla fija.

Normalmente, los objetos de Servicio son mejores para sistemas de medianos a grandes: aquellos con una cantidad decente de lógica, más allá de las operaciones CRUD estándares.

Así que cuando pienses que un trozo del código no pertenece en el directorio, en el lugar donde lo ibas a agregar, es buena idea reconsiderarlo y ver si sería mejor que fuese a un objeto de servicio.

Aquí están algunos indicadores de cuándo usar objetos de Servicio:

  • La acción es compleja.
  • La acción alcanza múltiples modelos.
  • La acción interactúa con un servicio externo.
  • La acción no es una preocupación primordial del modelo subrayado.
  • Hay múltiples maneras de realizar la acción.

¿Cómo debes diseñar Objetos de Servicio?

Diseñar la clase para un objeto de servicio es relativamente directo, ya que no necesitas gems especiales, tampoco debes aprender un DLS nuevo, pero si puedes, más o menos, confiar en las habilidades de diseño software que ya posees.

Usualmente, utilizo las siguientes directrices y convenciones para diseñar el objeto de servicio:

  • No almacenes el estado del objeto.
  • Usa métodos de instancia, no métodos de clase.
  • Debe haber muy pocos métodos públicos (preferiblemente uno que apoye *SRP.
  • Los métodos deben regresar resultados de objeto ricos, no booleanos.
  • Los servicios van debajo del directorio app/services . Te aconsejo que uses subdirectorios, para dominios lógicas de negocio fuertes. Por ejemplo, el archivo app/services/report/generate_weekly.rb definirá Report::GenerateWeekly mientras que app/services/report/publish_monthly.rb definirá Report::PublishMonthly.
  • Los servicios comienzan con un verbo (y no terminan en servicio): ApproveTransaction, SendTestNewsletter, ImportUsersFromCsv.
  • Los servicios responden al método llamada. Me di cuenta que usar otro verbo lo hace un poco redundante: ApproveTransaction.approve() no se lee bien. También, el método llamada, es el método de facto de lambda, procs, y objetos de método.

Si observas StatisticsController#index, notarás un grupo de métodos (weeks_to_date_from, weeks_to_date_to, avg_distance, etc.) agrupados al controlador. Eso no es bueno. Considera las ramificaciones, si quieres generar el informe semanal fuera de statistics_controller.

En nuestro caso, vamos a crear Report::GenerateWeekly y extraigamos el informe de lógica de StatisticsController:

Así que StatisticsController#index ahora se ve más limpio:

Al aplicar el patrón del objeto de Servicio, agrupamos código alrededor de una acción compleja y específica y promovemos la creación de métodos pequeños y más claros.

Tarea: considera usar Objeto de Valor para el WeeklyReport en vez de Struct.

Like what you’re reading?
Get the latest updates first.
No spam. Just great engineering posts.
Like what you’re reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Extrae Objetos Query De Los Controladores

¿Qué es un Objeto Query?

Un objeto Query es un PORO, el cual representa una base de datos de consulta. Puede ser reusada en diferentes lugares de la aplicación, mientras que, al mismo tiempo, esconde la lógica de consulta. También provee una buena unidad aislada para pruebas.

Deberías extraer consultas SQL/NoSQL complejas hacia sus propias clases.

Cada objeto Query es responsable de regresar un set de resultados basado en las reglas de criterio/negocio.

En este ejemplo, no tenemos ninguna consulta (query) compleja, así que usar objeto Query no sería eficiente. Sin embargo, con el fin de demostrar, extraeremos la consulta en Report::GenerateWeekly#call y crearemos generate_entries_query.rb:

Y en Report::GenerateWeekly#call, reemplacemos:

 def call
   @user.entries.group_by(&:week).map do |week, entries|
     WeeklyReport.new(
      ...
     )
   end
 end

con:

 def call
   weekly_grouped_entries = GroupEntriesQuery.new(@user).call

   weekly_grouped_entries.map do |week, entries|
     WeeklyReport.new(
      ...
     )
   end
 end

El patrón de objeto query (consulta) ayuda a mantener la lógica de tu modelo estrictamente relacionada a un comportamiento de clase, mientras que mantiene tus controladores flacas. Debido a que no son más que plain old Ruby classes, los objetos query no necesitan heredar de ActiveRecord::Base, y deberían ser responsables por nada más que la ejecución de consultas.

Extrae Crear Entrada A Un Objeto de Servicio

Ahora, vamos a extraer la lógica de crear una nueva entrada a un nuevo objeto de servicio. Vamos a usar la convención y creemos CreateEntry:

Y ahora nuestro EntriesController#create es de la siguiente manera:

 def create
   begin
     CreateEntry.new(current_user, entry_params).call
     flash[:notice] = 'Entry was successfully created.'
   rescue Exception => e
     flash[:error] = e.message
   end

   redirect_to root_path
 end

Más Validaciones A Un Objeto De Forma

Ahora las cosas comienzan a ponerse más interesantes.

Recuerda que en nuestras directrices acordamos que queríamos que los modelos tuvieran asociaciones y constantes, pero nada más (ni validaciones ni llamados). Así que empecemos por remover los llamados y usa un objeto de Forma en su lugar.

Un objeto de Forma es un PORO (Plain Old Ruby Object). Toma el mando del controlador/objeto de servicio cuando necesite hablar con la base de datos.

¿Por qué usar objetos de Forma?

Cuando necesites refactorizar tu aplicación, siempre es buena idea tener en mente, la responsabilidad única principal (SRP).

SRP te ayuda a tomar mejores decisiones de diseño, en cuanto a la responsabilidad que debe tener una clase.

Tu modelo de mesa de base de datos (un modelo ActiveRecord en el contexto de Rails), por ejemplo, representa un record de la base de datos único en código, así que no hay razón para que esté preocupado con nada que haga tu usuario.

Aquí es donde entra el objeto de Forma.

Un objeto de Forma es responsable de representar una forma en tu aplicación. Así que cada campo de entrada puede ser tratado como un atributo en la clase. Puede validar que esos atributos cumplen algunas reglas de validación, y puede pasar la data “limpia” a donde debe ir (ej., tu modelo de base de datos o tal vez tu constructor de búsqueda de consultas).

¿Cuándo deberías usar un objeto de Forma?

  • Cuando quieras extraer las validaciones de los modelos Rails.
  • Cuando múltiples modelos pueden ser actualizados por una sola forma de entrega, deberías crear un objeto de Forma.

Esto te permite poner toda la lógica de forma (nombrar convenciones, validaciones, y otros) en un solo lugar.

¿Cómo crear un objeto de Forma?

  • Crea una clase simple Ruby.
  • Incluye ActiveModel::Model (en Rails 3, tienes que incluir Nombre, Conversión y Validación, en su lugar).
  • Empieza a usar tu nueva clase de forma, como si fuera un modelo regular de ActiveRecord, donde la mayor diferencia es que no puedes continuar con la data almacenada en este objeto.

Por favor, ten en cuenta que puedes usar la gem reform, pero siguiendo con PORO, crearemos entry_form.rb lo cual se ve así:

Y modificaremos CreateEntry para comenzar a usar el objeto de Formato EntryForm:

     class CreateEntry
      
      ......
      ......

       def call
         @entry_form = ::EntryForm.new(@params)

         if @entry_form.valid?
            ....
         else
            ....
         end
       end
     end

Nota: Algunos de ustedes dirán que no hay necesidad de acceder al objeto de Forma desde el objeto de Servicio y que podemos llamar al objeto de Forma directamente desde el controlador, lo cual es un argumento válido. Sin embargo, preferiría tener un flujo claro, por eso siempre llamo al objeto de Forma desde objeto de Servicio.

Mueve los Llamados al Objeto de Servicio.

Como acordamos anteriormente, no queremos que nuestros modelos contengan validaciones y llamados. Extrajimos las validaciones usando objetos de Forma. Pero todavía estamos usando algunos llamados (after_create en modelo Entry compare_speed_and_notify_user).

¿Por qué queremos remover los llamados de los modelos?

Desarrolladores Rails usualmente comienzan a notar un dolor con los llamados, durante las pruebas. Si no estás haciendo pruebas con tus modelos ActiveRecord, comenzarás a notar el dolor después, mientras crece tu aplicación y mientras se necesite más lógica para llamar o evitar los llamados.

después_* los llamados son usados primordialmente en relación a guardar o continuar con el objeto.

Una vez que el objeto es guardado, el propósito (ej. responsabilidad) del objeto ha sido cumplido. Así que, si todavía vemos llamados siendo invocados, después de que el objeto ha sido guardado, estos probablemente son llamados que buscan salir del área de responsabilidad de objetos, y ahí es cuando encontramos problemas.

En nuestro caso, estamos enviando un SMS al usuario, lo que no está relacionado con el dominio de Entrada.

Una manera simple de resolver el problema es, mover el llamado al objeto de servicio relacionado. Después de todo, enviar un SMS para el usuario correspondiente está relacionado al Objeto de Servicio CreateEntry y no al modelo Entrada, como tal.

Al hacer esto, ya no tenemos que apagar, el método compare_speed_and_notify_user en nuestras pruebas. Hemos hecho que esto sea un asunto sencillo, el crear una entrada sin que sea necesario enviar un SMS y estamos siguiendo un buen diseño de Objeto Orientado, al asegurarnos de que nuestras clases tengan una responsabilidad única (SRP).

Así que ahora CreateEntry es algo similar a esto:

Usa Decoradores En Vez De Helpers

Aunque podemos fácilmente usar la colección Draper de modelos de vista y decoradores, me quedo con PORO, por este artículo, como lo he estado haciendo hasta ahora.

Lo que necesito es una clase que llame métodos al objeto decorado.

Puedo usar method_missing para implementar eso, pero usaré la biblioteca estándar de Ruby, SimpleDelegator. El siguiente código muestra cómo usar SimpleDelegator para implementar nuestro decorador base:

   % app/decorators/base_decorator.rb
   require 'delegate'

   class BaseDecorator < SimpleDelegator
     def initialize(base, view_context)
       super(base)
       @object = base
       @view_context = view_context
     end

     private

     def self.decorates(name)
       define_method(name) do
         @object
       end
     end

     def _h
       @view_context
     end
   end

¿Por qué el método _h?

Este método actúa como un proxy para contexto de vista. Por defecto, el contexto de vista es una instancia de una clase vista, siendo ésta ActionView::Base. Puedes acceder a los helpers de vistas de la siguiente manera:

   _h.content_tag :div, 'my-div', class: 'my-class'

Para hacerlo más conveniente agregamos un método decorado a ApplicationHelper:

   module ApplicationHelper

     # .....

     def decorate(object, klass = nil)
       klass ||= "#{object.class}Decorator".constantize
       decorator = klass.new(object, self)
       yield decorator if block_given?
       decorator
     end

     # .....

   end

Ahora, podemos mover los helpers EntriesHelper a los decoradores:

   # app/decorators/entry_decorator.rb
   class EntryDecorator < BaseDecorator
     decorates :entry

     def readable_time_period
       mins = entry.time_period
       return Time.at(60 * mins).utc.strftime('%M <small>Mins</small>').html_safe if mins < 60
       Time.at(60 * mins).utc.strftime('%H <small>Hour</small> %M <small>Mins</small>').html_safe
     end

     def readable_speed
       "#{sprintf('%0.2f', entry.speed)} <small>Km/H</small>".html_safe
     end
   end

Y podemos usar readable_time_period y readable_speed de la siguiente forma:

   # app/views/entries/_entry.html.erb
   -  <td><%= readable_speed(entry) %> </td>
   +  <td><%= decorate(entry).readable_speed %> </td>
   -  <td><%= readable_time_period(entry) %></td>
   +  <td><%= decorate(entry).readable_time_period %></td>

Estructura Después De Refactorizar

Terminamos con más archivos, pero eso no es necesariamente algo malo (y recuerda esto, desde el comienzo, estábamos conscientes de que este ejemplo era con fines demostrativos y no era necesariamente un buen caso de uso para refactorización):

   app
   ├── assets
   │   └── ...
   ├── controllers
   │   ├── application_controller.rb
   │   ├── entries_controller.rb
   │   └── statistics_controller.rb
   ├── decorators
   │   ├── base_decorator.rb
   │   └── entry_decorator.rb
   ├── forms
   │   └── entry_form.rb
   ├── helpers
   │   └── application_helper.rb
   ├── mailers
   ├── models
   │   ├── entry.rb
   │   ├── entry_status.rb
   │   └── user.rb
   ├── queries
   │   └── group_entries_query.rb
   ├── services
   │   ├── create_entry.rb
   │   └── report
   │       └── generate_weekly.rb
   └── views
       ├── devise
       │   └── ..
       ├── entries
       │   ├── _entry.html.erb
       │   ├── _form.html.erb
       │   └── index.html.erb
       ├── layouts
       │   └── application.html.erb
       └── statistics
           └── index.html.erb

Conclusión

Aunque nos enfocamos en Rails en este blog post, RoR (Ruby on Rails) no es una dependencia de los objetos de servicio ni de otros POROs. Puedes usar este enfoque con cualquier framework web, móvil o aplicación de consola.

Al usar MVC como arquitectura, todo se mantiene junto y te hace ir más lento porque la mayoría de los cambios tienen un impacto en otras partes de la aplicación. También te obliga a pensar donde poner algunas lógicas de negocio – ¿debería ir en el modelo, el controlador o la vista?

Al usar un simple PORO, hemos movido la lógica de negocio a los modelos o servicios, que no heredan de ActiveRecord, lo cual ya es una ganancia, sin mencionar que tenemos un código más claro, lo cual apoya SRP y pruebas de unidades más rápidas.

Una arquitectura limpia intenta poner las casillas de uso en el centro/parte superior de tu estructura para que veas fácilmente lo que hace tu aplicación. También facilita la adopción de cambios, porque es más modular y aislado. Espero haber demostrado como al Plain Old Ruby Objects y más abstracciones, separa preocupaciones, simplifica pruebas y ayuda a producir código limpio y fácil de mantener.

 

Contenido extraído de:  https://www.toptal.com/ruby-on-rails/construye-elegantes-componentes-rails-con-plain-old-ruby-objects/es
Autor: Eqbal Quran

https://www.toptal.com/resume/eqbal-quran

]]>
http://csipanama.com/2017/05/01/construye-elegantes-componentes-rails-con-plain-old-ruby-objects/feed/ 0
¿cuál es el mejor sistema de control de versiones? http://csipanama.com/2017/04/06/cual-es-el-mejor-sistema-de-control-de-versiones/ http://csipanama.com/2017/04/06/cual-es-el-mejor-sistema-de-control-de-versiones/#respond Fri, 07 Apr 2017 03:40:33 +0000 http://csipanama.com/?p=157 leer mas →]]>

Git vs. SVN: ¿cuál es el mejor sistema de control de versiones?

Los llamados sistemas de control de versiones fueron creados con el fin de detectar cambios en los documentos o archivos y se encargan de guardar todas las versiones anteriores, incluyendo el registro de fecha y hora, así como el identificador del usuario de un archivo para que los datos puedan ser recuperados y restaurados en cualquier momento. De esta forma, es posible determinar qué usuario ha realizado cambios en un punto determinado. Los objetivos generales de este tipo de sistemas consisten en coordinar el acceso compartido de varios usuarios a los archivos y permitir el desarrollo simultáneo de varias bifurcaciones o branches.

Generalmente, los sistemas de control de versiones se utilizan para el desarrollo de software, para aplicaciones de oficina y para gestores de contenido. Dos de los más conocidos son Apache Subversion (SVN) y Git, los cuales pueden ser instalados internamente en el servidor propio o externamente en el servidor de algún proveedor de alojamiento web. El servicio de alojamiento basado en la web para proyectos Git es GitHub, mientras que en  RiouxSVN aloja a Subversion. Proveedores como SourceForge ofrecen alojamiento para ambos sistemas.

SVN: el sucesor CVS de CollabNet

A principios del año 2000, CollabNet empezó a desarrollar el software libre Subversion y publicaría, cuatro años más tarde, su primera versión. Con ello, SVN se unió al modelo CVS (Concurrent Versions System). En 2009, el proyecto se trasladó a la Apache Software Foundation, motivo por el cual es conocido actualmente como Apache Subversion.

SVN se basa en un sistema de control de versiones centralizado. Esto significa que existe un almacén central de datos (el repositorio) accesible a todos los usuarios. Dado que los cambios realizados no pueden ser fusionados entre sí, el sistema evita que dos usuarios puedan editar un mismo archivo al mismo tiempo. El proceso es muy simple, cuando uno de los usuarios accede a un archivo, el sistema lo marca automáticamente como de solo lectura para los demás. Además, Apache Subversion ofrece la posibilidad de descargar y editar directorios individuales sin depender del árbol general de directorios. De esta manera, es posible asignar diferentes permisos de lectura y escritura a los diferentes usuarios. Subversion se caracteriza también porque puede registrar directorios vacíos, renombrados y mudados de sitio sin pérdidas de su historia.

Git: el sustituto de los desarrolladores del kernel de Linux

Casi de manera involuntaria, en abril de 2005 el creador de Linux, Linus Torvalds, comenzó con el desarrollo de un nuevo sistema de control de versiones. La razón: debido a un cambio en la licencia del sistema BitKeeper, los desarrolladores del núcleo de Linux perdieron su privilegio de uso gratuito. El nuevo sistema debía proporcionar flujos de trabajo similares a los de Bitkeeper y ofrecer un alto nivel de eficiencia, así como seguridad frente a cambios accidentales o intencionales.

Git es un sistema de control de versiones distribuido, lo que significa que, aunque existe un repositorio central en el cual se incorporan los cambios, todos los usuarios pueden descargar su propia copia de trabajo. De esta forma, todos tienen acceso al repositorio completo, incluyendo el historial local, sin depender de ningún tipo de conexión de red. Todos los cambios se transfieren rápidamente al repositorio central. Como consecuencia, Git no ofrece ningún sistema de bloqueo, sino que cada usuario genera sus propios directorios o branches dentro del árbol para ser cargados posteriormente al repositorio central. Por defecto, cada usuario tiene permisos de lectura y escritura para los diferentes directorios (en caso de que se quiera asignar permisos especiales, será necesario crear otros directorios raíz). Cada copia de trabajo es una copia de seguridad independiente del directorio raíz, lo que resulta ventajoso si este sufre algún daño o fallo. Git solo registra los contenidos de los directorios, por eso los vacíos se eliminan automáticamente.

SVN vs. Git: una comparación directa

Aunque muchos usuarios se preguntan cuál de los dos programas de control de versiones es mejor, no existe una respuesta general. La elección del sistema de control de versiones más adecuado para uno u otro proyecto dependerá de tus objetivos específicos. Ambos sistemas difieren en su estructura y en el proceso de trabajo resultante. La siguiente tabla resume sus principales diferencias:

SVN Git
Control de versiones Centralizada Distribuida
Repositorio Un repositorio central donde se generan copias de trabajo Copias locales del repositorio en las que se trabaja directamente
Autorización de acceso Dependiendo de la ruta de acceso Para la totalidad del directorio
Seguimiento de cambios Basado en archivos Basado en contenido
Historial de cambios Solo en el repositorio completo, las copias de trabajo incluyen únicamente la versión más reciente Tanto el repositorio como las copias de trabajo individuales incluyen el historial completo
Conectividad de red Con cada acceso Solo necesario para la sincronización

Estas son las principales ventajas de ambos sistemas:

Debes decantarte por Git cuando…

  • …no quieres depender de una conexión de red permanente, pues quieres trabajar en tu proyecto desde cualquier lugar.
  • …quieres seguridad en caso de fallo o pérdida de los repositorios principales.
  • …no necesitas contar con permisos especiales de lectura y escritura para los diferentes directorios (aunque, de ser así, será posible y complejo implementarlo).
  • …la transmisión rápida de los cambios es una de tus prioridades.

Subversion será la opción indicada, si…

  • …necesitas permisos de acceso basados en rutas de acceso para las diferentes áreas de tu proyecto.
  • …deseas agrupar todo tu trabajo en un solo lugar.
  • …trabajas con numerosos archivos binarios de gran tamaño.
  • …también quieres guardar la estructura de los directorios vacíos (estos son rechazados por Git, debido a que no contienen ningún tipo de contenido).

En caso de que las características enumeradas anteriormente no hayan sido decisivas para decantarse por alguno de los dos, siempre es recomendable realizar una prueba con cualquiera de los sistemas de control de versiones. En ambos casos encontrarás el apoyo de una gran comunidad, proveedores de alojamiento de gran calidad como GitHub y ofertas de soporte profesional.

]]>
http://csipanama.com/2017/04/06/cual-es-el-mejor-sistema-de-control-de-versiones/feed/ 0
¿Por qué programo mejor de noche? http://csipanama.com/2017/04/06/por-que-programo-mejor-de-noche/ http://csipanama.com/2017/04/06/por-que-programo-mejor-de-noche/#respond Fri, 07 Apr 2017 03:34:13 +0000 http://csipanama.com/?p=154 leer mas →]]> Los profesionales del desarrollo, programadores, gurus del código, en gran porcentaje trabajan mejor de noche.  Podemos preguntarle a cualquier desarrollador y en mas de un 90% de las veces les reponderá que sus mejores horas de desarrollo son durante la madrugada. En mi caso entre las 12:00 a.m. y 4:00 p.m.

Buscando en internet para responder esta duda, me encontré con un artículo de http://www.accionpreferente.com,  escrito por Enrique Lyon que me parecío muy intersante.

Concuerdo en un 100% con las observaciones realizadas en la nota, por la que se las reprodusco a continuación.

Un dicho popular dice que los programadores son máquinas que convierten la cafeína en códigos.

Y, por supuesto, si le preguntas a cualquier programador al azar cuándo hace su mejor trabajo, hay una alta posibilidad de que admita que lo hace de noche. Algunos más temprano, otros más tarde. Una tendencia popular es levantarse a las 4 am y trabajar un poco antes de que la locura del día comience. Otros, se van a la cama a las 4 am.

La esencia de todo esto es evitar las distracciones. Pero podrían simplemente cerrar la puerta, ¿qué tiene de especial la noche?

Creo que todo se reduce a tres cosas: el horario del fabricante, el cerebro somnoliento y pantallas de computador brillantes.

El horario del fabricante

Paul Graham escribió acerca del horario del fabricante en 2009 -, básicamente, que hay dos tipos de horarios en este mundo. El horario tradicional del gerente, donde el día se corta en horas, y diez minutos de distracción le cuesta, como máximo, el valor de una hora de tiempo.

Por otra parte, tienes algo que PG llama el horario del fabricante – un horario para aquellos de nosotros que producimos cosas. Trabajar en grandes sistemas abstractos implica ajustar toda la cosa en tu mente – alguien alguna vez comparó esto a la construcción de una casa de un caro cristal; tan pronto como alguien te distrae, todo se va hacia abajo y se rompe en mil pedazos.

Es por esto que los programadores se molestan tanto cuando los distraes.

Debido a esta enorme inversión mental, simplemente no podemos empezar a trabajar hasta que podamos esperar un par de horas sin que nos distraigan. Simplemente no vale la pena construir todo el modelo en tu cabeza para que luego se derribe media hora más tarde.

De hecho, hablando con muchos te darás cuenta de que sienten como si simplemente no pueden conseguir hacer ningún trabajo durante el día. El constante bombardeo de las interrupciones, los mensajes de correo electrónico para responder, simplemente no lo permiten. Así que realizan la mayor parte de su “trabajo trabajo” durante la noche cuando todo el mundo está durmiendo.

El cerebro somnoliento

Captura de pantalla 2014-07-11 a la(s) 10.43.24Pero incluso los programadores deberían estar durmiendo en la noche. No somos una raza de súper humanos. Incluso los programadores se sienten más alerta durante el día.

¿Por qué entonces llevamos a cabo nuestro trabajo mental más complejo cuando nuestro cerebro quiere dormir y hacemos tareas más simples, cuando nuestro cerebro está
más nítido y brillante?

Debido a que el cansancio nos hace mejores codificadores.

Al igual que en el pico de Ballmer, el cansancio nos puede hacer enfocar mejor, ¡simplemente porque cuando tu cerebro está cansado tiene que concentrarse! No queda suficiente capacidad intelectual para permitirse perder concentración.

Yo parezco conseguir menor trabajo bien hecho después de beber demasiado té o beber una bebida energética. Me hace hiperactivo y en un segundo estoy metiéndome a Twitter, viendo noticias de hackers, y simplemente parezco estar zumbando por todo el lugar..

Uno pensaría que trabajaría mejor – tanta energía, tanta capacidad intelectual estimulada. Pero, en cambio, me tropiezo conmigo mismo, porque no me puedo concentrar por más de dos segundos a la vez.

A la inversa, cuando estoy un poco cansado, acabo sentándome y codificando. Con un cerebro un poco cansado puedo codificar durante horas y horas sin siquiera pensar en la comprobación de Twitter o Facebook. Es como si Internet parara de existir.

Siento que esto es cierto para la mayoría de los programadores. Tenemos demasiada capacidad intelectual en aproximadamente el 80 por ciento de las tareas que realizamos – enfréntalo, escribir ese algoritmo, requiere de diez veces más para producir el entorno en el que puede funcionar. Incluso si estás haciendo la máquina más avanzada de aprendizaje (o algo así) que se pueda imaginar, una gran parte del trabajo no es más que limpieza de los datos y presentar los resultados de una manera amena.

Y cuando tu cerebro no está funcionando a plena capacidad busca algo que hacer. Estar cansado te hace tan tonto que la tarea que tienes mano es suficiente.

Las pantallas de computador brillantes

Esta es bastante simple. Sigue mirando hacia una fuente de luz brillante en la noche y tu ciclo de sueño se retrasará. Te olvidas de estar cansado hasta las 3am. Luego te despiertas a las 11am y en la tarde no estás cansado, porque oye, ¡sólo has estado despierto desde las 11am!

Dadas las suficientes iteraciones esto puede esencialmente arrastrarte a una zona horaria diferente. Lo que es más interesante es que no parece seguir rodando, una vez que llegas a ese equilibrio de irte a la cama entre las 3am y 4am, tiendes a permanecer allí.

O tal vez eso son sólo los relojes de alarma que hacen sus cosas porque la sociedad nos dice que somos unos vagos sucios si tomamos desayuno a las 2pm.

Conclusión

Para concluir, los programadores trabajan de noche, ya que no impone un límite de tiempo para dejar de trabajar, lo que les da un enfoque más relajado, su cerebro no sigue buscando distracciones y una pantalla brillante los mantiene despiertos.

]]>
http://csipanama.com/2017/04/06/por-que-programo-mejor-de-noche/feed/ 0
Código Buggy Python: Los 10 Errores más Comunes que Cometen los Desarrolladores Python http://csipanama.com/2017/04/06/codigo-buggy-python-los-10-errores-mas-comunes-que-cometen-los-desarrolladores-python/ http://csipanama.com/2017/04/06/codigo-buggy-python-los-10-errores-mas-comunes-que-cometen-los-desarrolladores-python/#respond Fri, 07 Apr 2017 03:19:07 +0000 http://csipanama.com/?p=150 leer mas →]]> Acerca de Python

Python es un lenguaje de programación interpretado y orientado a objetos de alto nivel con semántica dinámica. Su alto nivel integrado en las estructuras de datos, combinado con escritura y binding dinámicos lo hacen muy atractivo para el desarrollo rápido de aplicaciones, así como para su uso como lenguaje de script o glue para conectar componentes o servicios existentes. Python trabaja con módulos y paquetes, fomentando así la modularidad del programa y la reutilización de código.

Sobre este Artículo

La sintaxis simple y fácil de aprender de Python, puede enviar a los desarrolladores de Python en la dirección incorrecta – especialmente aquellos que están conociendo la lengua – perdiendo en el camino algunas de sus sutilezas y subestimando el poder del lenguaje diverso de Python.

Con esto en mente, este artículo presenta una lista “top 10” de errores sutiles, y difíciles de ver, que pueden tomar desprevenidos incluso a algunos de los desarrolladores de Python más avanzados.

(Nota: Este artículo está dirigido a un público más avanzado que el de Errores Comunes de Programadores Python, que se orienta más hacia aquellos que son nuevos en la lengua.)

Error común # 1: Un Mal Uso de Expresiones como Valores Predeterminados para los Argumentos de la Función

Python permite especificar que un argumento de la función es opcional, proporcionando un valor por defecto para ello. Si bien ésta es una gran característica de la lengua, puede dar lugar a cierta confusión cuando el valor por defecto es mutable. Por ejemplo, considera ésta definición de la función de Python:

>>> def foo(bar=[]):        # bar is optional and defaults to [] if not specified
...    bar.append("baz")    # but this line could be problematic, as we'll see...
...    return bar

Un error común es pensar que el argumento opcional se establecerá en la expresión por defecto específica, cada vez que se llama la función sin necesidad de suministrar un valor para el argumento opcional. En el código anterior, por ejemplo, se podría esperar que llamar a foo() varias veces (es decir, sin especificar un argumento bar) siempre daría de regreso baz, ya que la hipótesis sería que cada vez que foo() se llama (sin un argumento bar especificado) bar está ajustado a [] (es decir, una nueva lista vacía).

Pero vamos a ver lo que realmente sucede cuando se hace esto:

>>> foo()
["baz"]
>>> foo()
["baz", "baz"]
>>> foo()
["baz", "baz", "baz"]

¿Eh? ¿Por qué se siguió añadiendo el valor predeterminado de baz a una lista existente cada vez que foo() era llamado, en lugar de crear una nueva lista en cada oportunidad? La respuesta más avanzada de programación Python es que, el valor por defecto para un argumento de función se evalúa sólo una vez, en el momento en que se define la función. Por lo tanto, el argumento bar se inicializa con su valor por defecto (es decir, una lista vacía) sólo cuando foo() se ha definido por primera vez, pero luego los llamados a foo() (es decir, sin un argumento bar especificado) seguirán utilizando la misma lista a la que bar fue inicializado originalmente.

Por cierto, una solución común para esto es la siguiente:

>>> def foo(bar=None):
...    if bar is None:		# or if not bar:
...        bar = []
...    bar.append("baz")
...    return bar
...
>>> foo()
["baz"]
>>> foo()
["baz"]
>>> foo()
["baz"]

Error común # 2: Uso Incorrecto de las Variables de Clase

Consideremos el siguiente ejemplo:

>>> class A(object):
...     x = 1
...
>>> class B(A):
...     pass
...
>>> class C(A):
...     pass
...
>>> print A.x, B.x, C.x
1 1 1

Tiene sentido.

>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1

Sí, de nuevo como se esperaba.

>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3

¿Qué es esto? Sólo cambiamos A.x ¿Por qué C.x cambió también?

En Python, las variables de clase se manejan internamente como diccionarios y siguen lo que se refiere a menudo como Method Resolution Order (MRO). Así que en el código anterior, ya que el atributo x no se encuentra en la clase C, se buscará en sus clases base (únicamente A en el ejemplo anterior, aunque Python apoya herencia múltiple). En otras palabras, C no tiene su propia propiedad x, independiente de A. Por lo tanto, las referencias a C.x son de hecho referencias a A.x. Esto causa un problema Python, a menos que se maneje adecuadamente. Más información sobre atributos de clase en Python.

Error común # 3: Especificación de Parámetros de Forma Incorrecta para un Bloque de Excepción

Supongamos que tienes el siguiente código:

>>> try:
...     l = ["a", "b"]
...     int(l[2])
... except ValueError, IndexError:  # To catch both exceptions, right?
...     pass
...
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
IndexError: list index out of range

El problema aquí es que el informe except no toma una lista de excepciones que se especifiquen de esta forma. Por el contrario, Python 2.x la sintaxis except Exception, e, se utiliza para enlazar la excepción al segundo parámetro opcional especificado (en éste caso e), con el fin de que esté disponible para una inspección adicional. Como resultado, en el código anterior, la excepción IndexError no está siendo capturada por el informe except; más bien, la excepción termina siendo enlazada a un parámetro llamado IndexError.

La forma correcta de capturar varias excepciones en un informe except, es especificar el primer parámetro como una tupla que contiene todas las excepciones a ser capturadas. Además, para la máxima portabilidad, utiliza la palabra clave as, ya que la sintaxis es apoyada por Python 2 y Python 3:

>>> try:
...     l = ["a", "b"]
...     int(l[2])
... except (ValueError, IndexError) as e:  
...     pass
...
>>>

Error común # 4: No Entender las Reglas de Ámbito de Python

La resolución de ámbito de Python se basa en lo que se conoce como la regla LEGB, que es la abreviatura de Local, Enclosing, Global, Built-in. Parece bastante sencillo, ¿verdad? Bueno, en realidad, hay algunas sutilezas en la forma en que esto funciona en Python, lo que nos lleva al problema común, más avanzado, de programación Python, a continuación.

Considera lo siguiente:

>>> x = 10
>>> def foo():
...     x += 1
...     print x
...
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment

¿Cuál es el problema?

El error anterior se debe a que, cuando se hace una asignación a una variable en un ámbito, esa variable es considerada, automáticamente por Python, como local en ese ámbito y sigue cualquier variable de nombre similar, en cualquier ámbito exterior.

Muchos de ellos son, por lo tanto, se sorprenden al conseguir un UnboundLocalError en el código de trabajo anterior, cuando éste se modifica al añadir una instrucción de informe, en alguna parte del cuerpo de una función. (Puedes leer más sobre esto aquí.)

Es particularmente común que esto confunda a los desarrolladores cuando usan listas. Considera el siguiente ejemplo:

>>> lst = [1, 2, 3]
>>> def foo1():
...     lst.append(5)   # This works ok...
...
>>> foo1()
>>> lst
[1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2():
...     lst += [5]      # ... but this bombs!
...
>>> foo2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment

¿Eh? ¿Por qué foo2 falló, mientras que foo1 funcionó muy bien?

La respuesta es la misma que en el problema del ejemplo anterior, pero es sin duda más sutil. foo1 No está haciendo una asignación a lst, mientras que foo2 sí lo está. Recordando que lst += [5] es en realidad la abreviatura de lst = lst + [5], vemos que estamos tratando de asignar un valor a lst (por lo tanto, Python presume que está en el ámbito local). Sin embargo, el valor que estamos tratando de asignar a lst se basa en el mismo lst (de nuevo, ahora presume estar en el ámbito local), que aún no ha sido definido. Boom.

Error común # 5: Modificar una Lista al Iterar Sobre Ella

El problema con el siguiente código debería ser bastante obvio:

>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> for i in range(len(numbers)):
...     if odd(numbers[i]):
...         del numbers[i]  # BAD: Deleting item from a list while iterating over it
...
Traceback (most recent call last):
  	  File "<stdin>", line 2, in <module>
IndexError: list index out of range

La eliminación de un elemento de una lista o matriz, mientras que se da iteración sobre ella, es un problema de Python que es bien conocido por cualquier desarrollador de software con experiencia. Sin embargo, aunque el ejemplo anterior puede ser bastante obvio, incluso los desarrolladores avanzados pueden involuntariamente, ser tomados por sorpresa por éste código, el cual es mucho más complejo.

Afortunadamente, Python incorpora una serie de paradigmas de programación elegantes que cuando se utilizan correctamente, pueden resultar en un código significativamente simplificado y racionalizado. Un beneficio secundario de esto es que el código más simple es menos probable que sea sorprendido por el bug eliminación-accidental-de-un-item-de-lista-mientras-iterando-sobre-ella. Uno de estos paradigmas es el de [comprensiones de lista]((https://docs.python.org/2/tutorial/datastructures.html#tut-listcomps). Por otra parte, las comprensiones de lista son particularmente útiles para evitar este problema específico, como se muestra en ésta implementación alternativa del código mostrado arriba, que funciona perfectamente:

>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> numbers[:] = [n for n in numbers if not odd(n)]  # ahh, the beauty of it all
>>> numbers
[0, 2, 4, 6, 8]
Like what you’re reading?
Get the latest updates first.
No spam. Just great engineering posts.
Like what you’re reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Error común # 6: La Confusión de Cómo Python Enlaza las Variables en los Cierres

Teniendo en cuenta el siguiente ejemplo:

>>> def create_multipliers():
...     return [lambda x : i * x for i in range(5)]
>>> for multiplier in create_multipliers():
...     print multiplier(2)
...

Deberías esperar el siguiente resultado:

0
2
4
6
8

Pero en realidad obtienes:

8
8
8
8
8

¡Sorpresa!

Esto sucede debido al comportamiento enlace tardío de Python, que dice que los valores de las variables utilizadas en los cierres, se buscan en el momento en el que se llama a la función interna. Así que en el código anterior, cuando cualquiera de las funciones devueltas es llamada, el valor de i se busca en el ámbito que lo rodea en el momento en que se llama (y en ese momento, el círculo se ha completado, por lo que a i ya se le ha asignado su valor final de 4).

La solución a este problema común Python es un poco de hack:

>>> def create_multipliers():
...     return [lambda x, i=i : i * x for i in range(5)]
...
>>> for multiplier in create_multipliers():
...     print multiplier(2)
...
0
2
4
6
8

¡Voilà! Estamos tomando ventaja de los argumentos por defecto para generar funciones anónimas, con el fin de lograr el comportamiento deseado. Algunos llamarían a esto, elegante. Algunos lo llamarían sutil. Algunos lo odian. Pero si eres un desarrollador de Python, es importante entender esto.

Error común # 7: Crear Dependencias de Módulos Circulares

Digamos que tienes dos archivos, a.py y b.py, cada uno de los cuales importa al otro, de la siguiente manera:

en a.py:

import b

def f():
    return b.x
	
print f()

Y en b.py:

import a

x = 1

def g():
    print a.f()

En primer lugar, vamos a tratar de importar a.py:

>>> import a
1

Funcionó muy bien. Tal vez te sorprendió. Después de todo, tenemos una importación circular aquí que presumiblemente debería ser un problema, ¿no?

La respuesta es que la mera presencia de una importación circular no es como tal un problema en Python. Si un módulo ya se ha importado, Python es lo suficientemente inteligente como para no intentar volverlo a importar. Sin embargo, dependiendo del punto en el que cada módulo está intentando acceder a las funciones o variables definidas en el otro, podrías tener problemas.

Así que volviendo a nuestro ejemplo, cuando importamos a.py, no tenía ningún problema al importar b.py, ya que b.py no requiere nada de b.py para definirse en el momento de su importación. La única referencia en b.py a a, es el llamado a a.f(). Pero ese llamado está en g() y nada en a.py o b.py invoca g(). Así que, la vida es bella.

Pero ¿qué ocurre si se intenta importar b.py (claro, sin haber previamente importado a.py):

>>> import b
Traceback (most recent call last):
  	  File "<stdin>", line 1, in <module>
  	  File "b.py", line 1, in <module>
    import a
  	  File "a.py", line 6, in <module>
	print f()
  	  File "a.py", line 4, in f
	return b.x
AttributeError: 'module' object has no attribute 'x'

Uh oh. ¡Eso no es bueno! El problema aquí es que, en el proceso de importación b.py, intenta importar a.py, lo que como resultado llama a f(), que a su vez intenta acceder a b.x. Pero b.x aún no ha sido definida. De ahí la excepción AttributeError.

Al menos una solución a esto es bastante trivial. Simplemente modifica b.py para importar a.py dentro de g():

x = 1

def g():
    import a	# This will be evaluated only when g() is called
    print a.f()

Cuando se importa, todo está bien:

>>> import b
>>> b.g()
1	# Printed a first time since module 'a' calls 'print f()' at the end
1	# Printed a second time, this one is our call to 'g'

Error común # 8: Choque de Nombres con Módulos de la Biblioteca Estándar de Python

Una de las ventajas de Python es la gran cantidad de módulos de biblioteca que trae desde el principio. Pero como resultado, si no estás evitando esto conscientemente, no es tan difícil encontrarse con un choque de nombres, entre el nombre de uno de tus módulos y un módulo con el mismo nombre en la biblioteca estándar que se incluye en Python (por ejemplo, es posible que tengas un módulo denominado email.py en tu código, lo que estaría en conflicto con el módulo de biblioteca estándar del mismo nombre).

Esto puede conducir a problemas muy agresivos, como la importación de otra biblioteca que a su vez intenta importar la versión de bibliotecas estándar Python de un módulo, pero como ya tienes un módulo con el mismo nombre, el otro paquete importa erróneamente tu versión, en lugar de la que se encuentra dentro de la biblioteca estándar Python, y es aquí es donde se producen los errores más graves.

Por lo tanto, se debe tener cuidado para evitar el uso de los mismos nombres que los de los módulos de biblioteca estándar Python. Es mucho más fácil para ti cambiar el nombre de un módulo dentro de tu paquete que presentar una propuesta de mejora de Python (PEP) para solicitar un cambio de nombre upstream y conseguir que lo aprueben.

Error común # 9: El No Poder Hacer Frente a las Diferencias entre Python 2 y Python 3

Considera el siguiente archivo foo.py:

import sys

def bar(i):
    if i == 1:
        raise KeyError(1)
    if i == 2:
        raise ValueError(2)

def bad():
    e = None
    try:
        bar(int(sys.argv[1]))
    except KeyError as e:
        print('key error')
    except ValueError as e:
        print('value error')
    print(e)

bad()

En Python 2, esto funciona muy bien:

$ python foo.py 1
key error
1
$ python foo.py 2
value error
2

Pero ahora vamos a dar un giro en Python 3:

$ python3 foo.py 1
key error
Traceback (most recent call last):
  File "foo.py", line 19, in <module>
    bad()
  File "foo.py", line 17, in bad
    print(e)
UnboundLocalError: local variable 'e' referenced before assignment

¿Qué acaba de ocurrir aquí? El “problema” es que en Python 3 el objeto de excepción no es accesible más allá del alcance del bloque except. (La razón de esto es que, de lo contrario, mantendría un ciclo de referencia con el marco de pila en la memoria hasta que se ejecute el recolector de basura y purgue las referencias de la memoria. Más detalles técnicos sobre esto están disponibles aquí).

Una forma de evitar este problema es mantener una referencia al objeto de excepción fuera del alcance del bloque except, de modo que siga siendo accesible. Aquí hay una versión del ejemplo anterior que utiliza esta técnica, por lo tanto, dosificando el código y haciéndole más compatible con Python 2 y Python 3:

import sys

def bar(i):
    if i == 1:
        raise KeyError(1)
    if i == 2:
        raise ValueError(2)

def good():
    exception = None
    try:
        bar(int(sys.argv[1]))
    except KeyError as e:
        exception = e
        print('key error')
    except ValueError as e:
        exception = e
        print('value error')
    print(exception)

good()

La ejecución de éste es en Py3k:

$ python3 foo.py 1
key error
1
$ python3 foo.py 2
value error
2

¡Yupi!

(Por cierto, nuestra Guía de contratación Python (Python Hiring Guide) analiza una serie de diferencias importantes, que se deben tener en cuenta al migrar el código de Python 2 a Python 3.)

Error común # 10: El Mal Uso del Método __del__

Digamos que tenías esto en un archivo llamado mod.py:

import foo

class Bar(object):
   	    ...
    def __del__(self):
        foo.cleanup(self.myhandle)

Y, luego, trataste de hacer esto desde another_mod.py:

import mod
mybar = mod.Bar()

Obtendrías una fea excepción AttributeError.

¿Por qué? Debido a que, como se informó aquí, al apagarse intérprete, las variables globales del módulo se ajustan a None. Como resultado, en el ejemplo anterior, en el punto que __del__ se invoca, el nombre foo ya se ha ajustado a None.

Una solución a este problema algo más avanzado que la programación Python, sería utilizar atexit.register() en su lugar. De esta manera, cuando el programa se haya ejecutado (al salir normalmente, quiero decir), tus gestores registrados son echados antes de que el intérprete se apague.

Con éste conocimiento, una solución para el anterior código mod.py podría ser algo como esto:

import foo
import atexit

def cleanup(handle):
    foo.cleanup(handle)


class Bar(object):
    def __init__(self):
        ...
        atexit.register(cleanup, self.myhandle)

Esta aplicación ofrece una manera limpia y confiable de llamar a cualquier funcionalidad de limpieza necesaria para después de la terminación normal del programa. Obviamente, le toca a foo.cleanup decidir qué hacer con el objeto unido al nombre self.myhandle, pero se entiende la idea.

Para Terminar

Python es un lenguaje potente y flexible con muchos mecanismos y paradigmas que pueden mejorar considerablemente la productividad. Sin embargo, al igual que con cualquier herramienta de software o lenguaje, el tener una limitada comprensión o apreciación de sus capacidades a veces puede ser más un impedimento que una ventaja, dejándonos en el estado proverbial de “saber lo suficiente como para ser peligroso”.

Familiarizándose con los matices clave de Python, tales como (pero de ninguna manera se limita a) los problemas de programación moderadamente avanzados planteados en este artículo, ayudará a optimizar el uso de la lengua, evitando algunos de sus errores más comunes.

Deberías revisar nuestra Guía a Entrevistas Python (Insider’s Guide to Python Interviewing), para obtener sugerencias sobre preguntas de entrevistas que pueden ayudar a identificar a los expertos en Python.

Esperamos que te sean útiles los consejos en este artículo y apreciamos tus comentarios.

 

Contenido extraído de:  https://www.toptal.com/python/c%C3%B3digo-buggy-python-los-10-errores-m%C3%A1s-comunes-que-cometen-los-desarrolladores-python/es
Autor: Martin Chikilian, Argentina

]]>
http://csipanama.com/2017/04/06/codigo-buggy-python-los-10-errores-mas-comunes-que-cometen-los-desarrolladores-python/feed/ 0
Un Tutorial Paso-a-Paso para tu Primera Aplicación AngularJS http://csipanama.com/2017/03/01/un-tutorial-paso-a-paso-para-tu-primera-aplicacion-angularjs/ http://csipanama.com/2017/03/01/un-tutorial-paso-a-paso-para-tu-primera-aplicacion-angularjs/#respond Wed, 01 Mar 2017 19:00:37 +0000 http://csipanama.com/?p=145 leer mas →]]> ¿Qué es AngularJS?

AngularJS es un marco de JavaScript MVC desarrollado por Google, el cual permite construir aplicaciones front-end bien estructuradas y fáciles de comprobar y mantener.

¿Y Por Qué Debo Utilizarlo?

Si no has probado AngularJS todavía, es una lástima. El marco consiste en un conjunto de herramientas bien integradas que te ayudará a construir aplicaciones del lado del cliente, bien estructuradas en un sistema modular, con menos código y más flexibilidad.

AngularJS extiende HTML, proporcionando directrices que añaden funcionalidad a tu margen de beneficio y te permite crear plantillas dinámicas poderosas. También puedes crear tus propias directrices, elaborando componentes reusables que completan tus necesidades y abstrayendo toda la lógica de manipulación del DOM.

También implementa binding de datos de dos vías, conectando tu HTML (vistas) a los objetos de JavaScript (modelos) sin problemas. En términos simples, esto significa que cualquier actualización de tu modelo se reflejará inmediatamente en tu vista, sin necesidad de ningún tipo de manipulación DOM o el control de eventos (por ejemplo, con jQuery).

Angular presta servicios en la parte superior de XHR que simplifican considerablemente tu código y permite abstraer llamadas API en servicios reusables. Con esto, puedes mover tu modelo y lógica de negocio para el front-end y construir aplicaciones web back-end independientes (agnostic).

Por último, me encanta Angular debido a su flexibilidad en cuanto a la comunicación del servidor. Como la mayoría de los marcos de JavaScript MVC, Angular te permite trabajar con cualquier tecnología de servidor, siempre que puede servir a tu aplicación a través de una API REST Web. Pero Angular también proporciona servicios aparte de XHR, los cuales simplifican considerablemente tu código y te permite abstraer llamadas API en servicios reusables. Como resultado, se puede mover el modelo y la lógica de negocio para el front-end y construir aplicaciones web back-end independientes. En este post vamos a hacer precisamente eso: un paso a la vez.

Así que, ¿Por Dónde Empezamos?

En primer lugar, vamos a decidir la naturaleza de la aplicación que queremos construir. En esta guía, preferimos no pasar demasiado tiempo en el back-end, por lo que vamos a escribir algo sobre la base de datos que es fácil de obtener en internet, ¡como una aplicación de noticias deportiva!

Ya que soy un gran fan del automovilismo y la Fórmula 1, voy a utilizar un servicio API Autosport como nuestro back-end. Por suerte, los chicos de Ergast son lo suficientemente amables para proporcionar una API de automovilismo gratis, la cual es perfecta para nosotros.

Como adelanto de lo que vamos a construir, echa un vistazo al demo en vivo. Para embellecer el demo y mostrar algunas plantillas Angular, apliqué un tema Bootstrap de WrapBootstrap, pero ya que éste artículo no es acerca de CSS, lo voy a abstraer de los ejemplos y dejarlo fuera por completo.

Tutorial para Comenzar

Vamos a iniciar nuestra aplicación de ejemplo con un poco de Boilerplate. Recomiendo el proyecto angular-seed, ya que no sólo proporciona un gran esqueleto para bootstrapping, sino que también establece las bases para las pruebas de unidad con Karma y Jasmine(no vamos a hacer ninguna prueba en éste demo, así que vamos dejar eso de lado por ahora; ve la Parte 2 de éste tutorial para obtener más información sobre la configuración de tu proyecto, para pruebas unitarias y de extremo a extremo).

EDITADO (Mayo de 2014): Desde que escribí éste tutorial, el proyecto angular-seed ha pasado por algunos cambios importantes (incluyendo la adición de Bower como gestor de paquetes). Si tienes alguna duda acerca de cómo implementar el proyecto, echa un vistazo rápido a la primera sección de su guía de referencia. En la parte 2 del tutorial, Bower, entre otras herramientas, es explicado en mayor detalle.

Bien, ahora que hemos clonado el repositorio e instalado las dependencias, el esqueleto de nuestra aplicación va a tener este aspecto:

Ahora podemos empezar a codificar. Como estamos tratando de construir aplicación de noticias de deporte para un campeonato de carreras, vamos a empezar con la vista más relevante: la tabla del campeonato.

Teniendo en cuenta que ya tenemos una lista de los conductores definida dentro de nuestro alcance (Quédate conmigo – Llegaremos ahí), y haciendo caso omiso de cualquier CSS (para facilitar la lectura), nuestra HTML podría ser:

<body ng-app="F1FeederApp" ng-controller="driversController">
  <table>
    <thead>
      <tr><th colspan="4">Drivers Championship Standings</th></tr>
    </thead>
    <tbody>
      <tr ng-repeat="driver in driversList">
        <td>{{$index + 1}}</td>
        <td>
          <img src="img/flags/{{driver.Driver.nationality}}.png" />
          {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
        </td>
        <td>{{driver.Constructors[0].name}}</td>
        <td>{{driver.points}}</td>
      </tr>
    </tbody>
  </table>
</body>

La primera cosa que notarás en esta plantilla es el uso de expresiones (“{{“ y “}}”) para regresar valores de las variables. En AngularJS, las expresiones permiten ejecutar algunos cálculos, con el fin de regresar un valor deseado. Algunas expresiones válidas serían:

  • {{ 1 + 1 }}
  • {{ 946757880 | date }}
  • {{ user.name }}

Efectivamente, las expresiones son fragmentos parecidos a JavaScript. A pesar de ser muy potente, no deberías utilizar expresiones para implementar cualquier lógica de nivel superior. Para ello, utilizamos directrices.

La Comprensión de las Directrices Básicas

La segunda cosa que notarás es la presencia de ng-attributes, que no verías en el marcado típico. Esas son las directrices.

En un nivel alto, las directrices son marcadores (como atributos, etiquetas y nombres genéricos) que le ordenan a AngularJS adjuntar un comportamiento dado a un elemento DOM (o transformarlo, reemplazarlo, etc.). Vamos a echar un vistazo a los que ya hemos visto:

  • La directriz ng-app es responsable de hacer bootstrapping a tu aplicación, para definir el ámbito de ésta. En AngularJS, puedes tener múltiples aplicaciones dentro de la misma página, por lo que esta directriz define el lugar donde comienza y termina cada aplicación.
  • La directriz ng-controller define qué controlador estará a cargo de tu vista. En este caso, la denotamos driversController, la cual proporcionará nuestra lista de conductores (driversList).
  • La directriz ng-repeat es una de las más utilizadas, y sirve para definir tu alcance de plantilla al pasar a través de colecciones. En el ejemplo anterior, repite una línea en la tabla por cada conductor en driversList.

Añadir Controladores

Por supuesto, nuestra vista no sirve de nada, sin un controlador. Vamos a añadir driversController a nuestros controllers.js:

angular.module('F1FeederApp.controllers', []).
controller('driversController', function($scope) {
    $scope.driversList = [
      {
          Driver: {
              givenName: 'Sebastian',
              familyName: 'Vettel'
          },
          points: 322,
          nationality: "German",
          Constructors: [
              {name: "Red Bull"}
          ]
      },
      {
          Driver: {
          givenName: 'Fernando',
              familyName: 'Alonso'
          },
          points: 207,
          nationality: "Spanish",
          Constructors: [
              {name: "Ferrari"}
          ]
      }
    ];
});

Seguro notaste la variable $scope que estamos pasando como parámetro al controlador. La variable $scope se supone que debe enlazar tu controlador y vistas. En particular, lleva todos los datos que se utilizarán dentro de la plantilla. Todo lo que se agrega a ella (como la driversList del ejemplo anterior) será directamente accesible en tus vistas. Por ahora, vamos a trabajar con una matriz de datos ficticios (estática), que vamos a sustituir más tarde con nuestro servicio API.

Ahora, añade esto a app.js:

angular.module('F1FeederApp', [
  'F1FeederApp.controllers'
]);

Con esta línea de código inicializamos nuestra aplicación y registramos los módulos de los cuales depende. Volveremos a ese archivo (app.js) más adelante.

Ahora, vamos a poner todo junto en index.html:

<!DOCTYPE html>
<html>
<head>
  <title>F-1 Feeder</title>
</head>

<body ng-app="F1FeederApp" ng-controller="driversController">
  <table>
    <thead>
      <tr><th colspan="4">Drivers Championship Standings</th></tr>
    </thead>
    <tbody>
      <tr ng-repeat="driver in driversList">
        <td>{{$index + 1}}</td>
        <td>
          <img src="img/flags/{{driver.Driver.nationality}}.png" />
          {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
        </td>
        <td>{{driver.Constructors[0].name}}</td>
        <td>{{driver.points}}</td>
      </tr>
    </tbody>
  </table>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-route/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/services.js"></script>
  <script src="js/controllers.js"></script>
</body>
</html>

Moduló errores menores, ahora puedes iniciar tu aplicación y comprobar tu lista (estática) de los conductores.

Nota: Si necesitas ayuda para la depuración de la aplicación y la visualización de tus modelos y con el alcance dentro del navegador, recomiendo echar un vistazo a la impresionante batarang; complemento para Chrome.

 

Contenido extraído de:  https://www.toptal.com/angular-js/un-tutorial-paso-a-paso-para-tu-primera-aplicaci%C3%B3n-angularjs/es
Autor: Raoni Boaventura

]]>
http://csipanama.com/2017/03/01/un-tutorial-paso-a-paso-para-tu-primera-aplicacion-angularjs/feed/ 0
Zend Server – La manera más rápida de implementar PHP en su empresa http://csipanama.com/2016/05/31/zend-server-la-manera-mas-rapida-de-implementar-php-en-su-empresa/ http://csipanama.com/2016/05/31/zend-server-la-manera-mas-rapida-de-implementar-php-en-su-empresa/#respond Tue, 31 May 2016 17:49:04 +0000 http://csipanama.com/?p=140 leer mas →]]> Hace pocos días, Zend Enterprice, la empresa reponsable por el desarrollo de PHP sacó al mercado su versión más reciente de ZEND SERVER.

Con Zend Server, los equipos de desarrollo y operaciones están equipados con la infraestructura de software, herramientas y mejores prácticas para la colaboración productiva y la entrega continua de sus aplicaciones móviles y web con un rendimiento excepcional, la fiabilidad y la seguridad.

Apoyando el explosivo rendimiento de PHP 7 con herramientas innovadoras para aislar rápidamente los problemas de producción, Zend Server es una plataforma probada para ayudar a las organizaciones a implementar rápidamente, reducir el tiempo de inactividad, y operar a escala de Internet.

Zend Server incluye más de 80 extensiones de PHP y soporta Apache, Nginx y los servidores web IIS, a veces referido como servidores PHP. Se entrega como paquetes nativos para todas las principales distribuciones de Linux, Windows, Mac OS X y entornos IBM i incluyendo entornos de nube populares como Amazon Web Services y Microsoft Azure. Zend Server es compatible con cualquier y todo el código PHP y proporciona una visión profunda de que el código que incluya las aplicaciones PHP más populares y framworks como WordPress , Magento , Drupal, Zend Framework , Symfony, y laravel. Y cualquier otro código que tiene en PHP se ejecutará en Zend Server.

 

¿Porqué Zend Server?

  • Galería de complementos impulsados ​​por la comunidad para la extensibilidad ilimitada
  • Con Z-Ray vision en su aplicación y code tracing para aumentar la calidad y el rendimiento
  • Funcionalidad de soporte en tiempo real para solucionar problemas de los usuarios en tiempo real,
  • Job queuing and almacenamiento en caché para optimizar y aumentar el rendimiento
  • El seguimiento y root-cause análisis
  • Capacidades de despliegue y DevOps para optimizar la entrega de aplicaciones
  • H/A agrupación de sesiones para una máxima escalabilidad y fiabilidad
  • Administración de multiples servidores y mas….

 

zend-server-9-blueprint_1

 

]]>
http://csipanama.com/2016/05/31/zend-server-la-manera-mas-rapida-de-implementar-php-en-su-empresa/feed/ 0
Órgano Judicial Gana con el Sistema Automatizado de Gestión Judicial – 2016 http://csipanama.com/2016/05/17/organo-judicial-gana-con-el-sistema-automatizado-de-gestion-judicial/ http://csipanama.com/2016/05/17/organo-judicial-gana-con-el-sistema-automatizado-de-gestion-judicial/#respond Tue, 17 May 2016 14:18:48 +0000 http://csipanama.com/?p=106 Minutos despues de publicada la nota anterior, me entero que el SAGJ gana como MEJOR PLATAFORMA TECNOLÓGICA INTERINSTITUCIONAL y PREMIO NACIONAL A LA INNOVACIÓN GUBERNAMENTAL.

CinXnI_WwAAnis5.jpg large

CincwXRWUAAOfFj.jpg large

Ciber Sistemas Inegrados, como proveedores del Sistema Automatizado de Gestión Judicial, no sentimos parte de este equipo, muy felices que el esfuerzo que todos hemos realizado rinda bellos frutos.  Gracias a la Secretaría Técnica de Modernización y Desarrollo Institucional por darnos la oportunidad de poner nuestro granito de arena para mejorar la justicia en Panamá.

Ver nota en el sitio de la Autoridad de Innovación Gubernamental

]]>
http://csipanama.com/2016/05/17/organo-judicial-gana-con-el-sistema-automatizado-de-gestion-judicial/feed/ 0
VI Premio Nacional a la Innovación Gubernamental http://csipanama.com/2016/05/17/vi-premio-nacional-a-la-innovacion-gubernamental/ http://csipanama.com/2016/05/17/vi-premio-nacional-a-la-innovacion-gubernamental/#respond Tue, 17 May 2016 14:09:28 +0000 http://csipanama.com/?p=102 En el día de ayer, uno de nuestros desarrollos, participa como proyecto por parte del Órgano Judicial dentro del marco de la entrega del Premio Nacional a la Innovación Gubernamental.  El Sistema Automatizado de Gestión Judicial, desarrollado por Ciber Sistemas Integrados.

Ya en una ocación fuimos galardonados con dicha distinción en el 2012 y esperamos este año, nuevamente salir triunfantes.

 

Nota de Prensa de DíaDía

]]>
http://csipanama.com/2016/05/17/vi-premio-nacional-a-la-innovacion-gubernamental/feed/ 0