Post 3: ¿Por qué no hago “coding challenges”?

enero 21, 2019

Es una práctica bastante extendida que las empresas subcontraten a otra empresa para hacer la búsqueda de talento cuando requieren contratar personal. La empresa que tiene el puesto vacante pasa a la agencia que hará la búsqueda de talento la descripción del puesto, los requisitos y el salario (o un rango salarial) y prestaciones que ofrecen.

Hace ya algún tiempo que empresas que buscan cubrir vacantes en el área de tecnologías de la información, específicamente para desarrollo de software, acostumbran incluir en los requisitos que se les pida a los candidatos hacer los llamados “coding challenges”, que básicamente consisten en hacer pequeños programas como una función, etc. en algún lenguaje de programación determinado.

Esto es aceptable cuando la empresa busca contratar un recién egresado o alguien que haga sus prácticas profesionales, porque desafortunadamente en México, el nivel de conocimientos de los egresados de alguna carrera relacionada con tecnologías de la información es bastante malo en la mayoría de los casos.

Lo que muchas de estas empresas parece que no han captado es que no puedes aplicar el mismo proceso de selección para alguien con experiencia. Cualquier profesional de TI enfocado en el desarrollo de software que se mantenga activo, normalmente tiene código disponible públicamente en internet al cual se puede tener acceso de forma gratuita, ya sea en una cuenta de github, en un blog, en youtube, etc. En el muy poco probable caso de que nunca hubiera hecho público algo de código programado por él, seguramente ha escrito muchos programas y podrá enviarle a la empresa el código fuente de alguno de ellos.

En lo personal yo he hecho público mucho código desarrollado por mi en este blog, y en otros lugares. Incluso el código fuente de mi proyecto final de maestría esta disponible en sourceforge.net. Cuando una empresa que me contacta para una vacante relacionada con desarrollo de software me pide que haga un “coding challenge” después de haber visto mi CV, sé que esa empresa no pone interés en los CV que recibe; ya que de ser así hubiera visto que en mi CV hay un enlace a mi blog, en el cual hay suficiente código.

No hago “coding challenges” porque si una empresa que busca ofrecerme un empleo que implique desarrollar software requiere pruebas de que sé programar, las pruebas están a la vista y ahí han estado durante años, si no quiere tomarse la molestia de revisarlas, entonces no me interesa, por lo que no tiene caso que los haga perder su tiempo ni que yo invierta el mio. Si me tratan como recién egresado, lo más probable es que me ofrezcan un salario de recién egresado, mismo que no voy a aceptar, así de simple.

El que alguien me pida que haga un examen de programación pudiendo ver todo el código que he puesto libremente y de manera gratuita a disposición del público en general, es tanto como decirme mentiroso, es una falta de respeto; en el mejor de los casos el mensaje que envía es de desconfianza y no estoy interesado en establecer una relación laboral (o de algún otro tipo) cuando hay desconfianza de la otra parte.

Anuncios

Post 2: ¿Cómo selecciono las herramientas que utilizo para desarrollar software?

enero 10, 2019

Debido a que existen muchos lenguajes de programación y herramientas para desarrollar software, es imposible saber todos los lenguajes y conocer todas las herramientas disponibles.

He visto que muchos desarrollan en lo que la mayoría usa o lo que está de moda.
En este post voy a describir cómo es que yo elegí algunos de los lenguajes de programación y herramientas que uso para desarrollar software.

Empece a crear programas para computadoras hace más de 30 años, el primer lenguaje de programación que aprendí fue Basic, porque era el único con el que podía programar la primer computadora a la que tuve acceso (mi hermano y yo vendimos nuestro Atari 2600 para comprar una computadora Commodore 16).

Después de varios años desarrollando software, ya sea en COBOL para una VAX 4200, o aplicaciones Desktop en Clipper, C++ y algunos otros lenguajes (antes no existía la web), llega el momento de tener que aprender a desarrollar aplicaciones web (no me refiero a una página HTML estática, sino a sistemas multiusuario con acceso a base de datos, etc.).

Aprendí Java e hice pruebas con servlets y Tomcat, no estuvo mal pero tenía que probar otros lenguajes y herramientas, así que probé con PHP; tampoco estuvo mal pero empezó a sonar por todos lados Ruby on Rails así que decidí probarlo. Leí la documentación del sitio web oficial, seguí unos tutoriales y un curso gratuito en internet y después me dispuse a hacer una aplicación (no es lo mismo un ejemplo de un tutorial o un curso que el desarrollo de un sistema real).

Una vez que ya había hecho el diagrama entidad-relación de la base de datos para la aplicación que iba a crear, empiezo a intentar crear los modelos en Rails y empiezo a descubrir algunos de los varios problemas que tiene ActiveRecord (el ORM que usa Rails), como el hecho de que la llave primaria de cada tabla tenga que estar formada por un sólo campo, que dicho campo deba llamarse id, ser de tipo entero y autoincrementable. Hay formas de “forzar” a Rails para que el campo que será la llave primaria no tenga que llamarse id (una forma de hacerlo se describe en este post http://ruby-journal.com/how-to-override-default-primary-key-id-in-rails/), pero hay otros problemas más, algunos de ellos los menciono en este post ¿Cómo diferenciar un buen desarrollador de software en Ruby del resto? en mi blog.

No tardé mucho tiempo en darme cuenta que debido a la filosofía de Rails de “Convention over Configuration”, estaba invirtiendo mucho tiempo tratando de implementar un modelo de base de datos bien diseñado en Rails. Así que en mi opinión Rails es un mal framework si quieres desarrollar aplicaciones bien hechas (con un buen diseño de base de datos para empezar). Busqué otros frameworks para desarrollo web con Ruby y vi algunos pero también eran “opinionated” como Rails, así que eso no me servía, hasta que descubrí Sinatra. En Sinatra no estás obligado a usar ActiveRecord, puedes usar otro ORM, así que intenté desarrollar mi aplicación utilizando Datamapper y pude implementar la base de datos tal y como la había diseñado, sin tener que estarme peleando con la herramienta.

La conclusión fue: Ruby si, Rails no.

No me gustan las herramientas que me quieren obligar a hacer las cosas de cierta manera, y menos si esa manera es la manera incorrecta de hacer las cosas. El hecho de que Rails te “obligue” a que la llave primaria de cada tabla esté formada por un solo campo llamado id de tipo entero autoincrementable porque “es más fácil” así, no significa que sea la forma correcta. Mi prioridad es hacer las cosas bien, no hacerlas de la forma que me resulte más fácil sin importar si están mal hechas. Pero resulta que puedas hacer las cosas bien y “fácil” si usas las herramientas correctas.

No está mal si un día usas Rails probablemente para hacer una aplicación que se va a usar una sóla vez y se necesita terminarla rápido, por ejemplo si necesitas crear un sistema para el registro de los asistentes a un congreso y ya tienes muy poco tiempo, ahí no importa mucho que la base de datos esté mal diseñada, es decir, como Rails la necesita para que funcione sin interponerse en tu camino.

Desarrollé unas cuantas aplicaciones con Sinatra y Datamapper (aplicaciones reales que actualmente están instaladas y funcionando), pero como siempre busco la mejor forma de hacer las cosas, encontré otro ORM llamado Sequel, que es más versátil que Datamapper, así que desarrollé una aplicación con Sinatra y Sequel para probarlo y quedé muy satisfecho. Desde entonces las aplicaciones web que he desarrollado (que también están instaladas y funcionando en producción) han sido utilizando Ruby con Sinatra y Sequel (además de otras herramientas desde luego).

Continuamente estoy aprendiendo y probando diferentes lenguajes y herramientas para desarrollo de software, desde luego pruebo lo que todos usan y pruebo alternativas y muy frecuentemente encuentro cosas mejores, y eso es lo que uso, no me importa que no sea lo que la mayoría usa. Yo uso lo que he probado que funciona mejor y con lo que me siento más cómodo trabajando. No decido usar un lenguaje de programación o herramienta sólo porque “hay muchas empresas que lo usan”, o “es lo que las empresas están solicitando si buscas trabajo”.

Como dice un refrán asiático: “Un tigre no pierde el sueño por la opinión de las ovejas”.

Otra combinación de lenguaje de programación y framework para desarrollo web que utilizaría es Python con Flask, (ya probé Django y aunque no es tan “opinionated” como Rails, no me ofrece la libertad que yo requiero para sentirme cómodo trabajando). No he tenido tiempo de hacer muchas pruebas con Flask pero lo poco que he probado me ha parecido bien.

Para concluir este post, mi recomendación es que si te interesa utilizar un lenguaje de programación o herramienta porque está de moda, te informes leyendo la documentación oficial, si te sigue llamando la atención lo aprendas y lo pruebes pero busques alternativas, probablemente encuentres algo mejor o que su performance y otras cualidades sean similares pero con lo cual te sientas más cómodo trabajando. Si lo encuentras, que no te importe que los demás te digan “pero la mayoría usa X en lugar de lo que tú estás usando” o algún otro argumento tonto; y si no lo encuentras, al menos estarás usando lo que usas no porque esté de moda sino porque realmente es lo que mejor funciona para ti.

Post 1: Las prácticas profesionales

enero 7, 2019

Recuerdo que en la universidad en donde estudié la licenciatura, a partir de quinto semestre podíamos realizar algo que llamaban “prácticas profesionales”, consistía en cubrir cierto número de horas trabajando en algún lugar y dicho lugar expedía un documento dirigido a la universidad en donde confirmaba que habías trabajado ahí dicho número de horas.

Estando en quinto semestre un compañero de carrera nos comento a otro estudiante y a mí que se había enterado que en una dependencia de gobierno existía la posibilidad de realizar las prácticas profesionales, fuimos los 3 y nos entrevistó una persona de dicha dependencia de gobierno.
Como yo quería que mis prácticas profesionales realmente me fueran útiles hice tres preguntas fundamentalmente.

  1.  ¿Qué trabajo es el que realizaría?
  2.  ¿Cuál es el horario?
  3.  ¿Hay alguna remuneración ?

No recuerdo las palabras exactas de las respuestas pero básicamente fue lo siguiente:

Respuestas a las preguntas 1 y 2.

Pues algunas veces tendrán que graficar datos en una hoja de cálculo, pero el trabajo realmente es estar aquí, si el “jefe” sigue en la oficina, no importa que ustedes no tengan trabajo asignado, tienen que estar aquí, porque en cualquier momento puede necesitar algo. Así que no hay hora de salida, la hora de salida es cuando hayan terminado lo que tenían asignado y el jefe ya se haya retirado.

Respuesta a la pregunta 3

No, no hay remuneración alguna

Desde ese momento yo di las gracias pero como mis compañeros (extrañamente para mi) si estuvieron interesados, esperé a que terminaran de ponerse de acuerdo con la persona que nos atendió para empezar a realizar sus prácticas profesionales en ese lugar.

Lo que pregunté era importante para mi porque muchos estudiantes que realizan sus prácticas profesionales, no hacen algo relacionado con su área de estudio, los ponen a fotocopiar documentos o hacer diligencias para los otros empleados del departamento (ir por las memelas, tacos, tamales, refrescos , etc.). El horario es importante porque debía tener libertad para seleccionar mi carga académica en la universidad y además, no estaba interesado en hacer mis prácticas profesionales en un lugar en donde te ven como esclavo y no como practicante.

Pregunté si había alguna remuneración porque desde luego no esperaba que fuera un sueldo, pero por lo menos que me dieran algo que me ayudara a pagar la gasolina o el pasaje para llegar ahí, no estaba interesado en hacer mis prácticas en un lugar en donde esperaran que yo tuviera que incurrir en gastos por ir a hacer trabajo para ellos.

Cuando los 3 nos retiramos del lugar, mis compañeros me decían que no podían creer que no hubiera aceptado y que nunca iba a encontrar un lugar en donde pagaran a las
personas que hacían sus prácticas profesionales. Yo les comenté que había tiempo todavía para buscar un lugar, creo que era posible hacer las prácticas profesionales hasta séptimo semestre, así que yo no tenía prisa. Un par de meses después un primo mío vio un anuncio en el periódico en donde solicitaban practicantes para el departamento de sistemas. Acudí a entrevista y las respuestas básicamente fueron las siguientes:

Respuesta a la pregunta 1

Vas a desarrollar reportes en COBOL con SQL

Respuesta a la pregunta 2

Tu pones tu horario pero debes cubrir un total de 20 horas a la semana (equivalente a 4 horas diarias de Lunes a Viernes)

Respuesta a la pregunta 3

Si, es una pequeña ayuda mensual

Como podrán imaginarse, hice mis prácticas profesionales en esa empresa.

Presentación de la categoría “I don’t lead, nor follow… I make my own path”

enero 7, 2019

Las personas que tenemos una mente curiosa generalmente somos como dice la canción Thunder de Imagine Dragons “Not a Yes Sir, not a follower”.
En lo personal esto me ha traído ciertas complicaciones desde que estudiaba la licenciatura en la universidad (posiblemente desde antes).

En esta categoría voy a escribir sobre algunas cosas que he vivido y que de alguna forma reflejan mi forma de pensar y de ser, sobre todo en el ámbito profesional. Probablemente le sea útil a los recién egresados de alguna licenciatura relacionada con tecnologías de la información, ya sea para aprender de mis errores y no cometerlos o para sentirse identificados si encuentran que han vivido algo similar.

Utilizando ZeroMQ en Ruby

noviembre 29, 2018

ZMQ (o ZeroMQ) es una biblioteca de mensajería desarrollada en C++. Es software libre que se distribuye bajo una licencia LGPL.

Existen bindings (APIs) para utilizar ZMQ en muchos lenguajes de programación, en éste artículo voy a mostrar un ejemplo utilizando Ruby.

Simula el desarrollo de un sistema para un restaurant; la idea es que en el área de cocina exista una computadora en donde estén llegando automáticamente las comandas que los meseros capturan en otra PC que se encuentra en el área de mesas.

La computadora que vamos a usar para simular la PC que estará en el área de cocina tiene instalado Debian 9 y la PC que utilizaremos para simular la computadora en donde los meseros capturan las comandas tiene instalado PointLinux (es una distribución basada en Debian).

En ambas PC debemos instalar Ruby, la bibilioteca zmq, el paquete ruby2.3-dev y la gema ffi-rzmq.

sudo apt install ruby
sudo apt install libzmq3-dev
sudo apt install ruby2.3-dev
sudo gem install ffi-rzmq

Aquí un video con la explicación y después los programas utilizados para cada una de las PC.

Programa servidorComandas.rb

#!/usr/bin/ruby
#encoding: utf-8

require 'ffi-rzmq'
require 'json'

# Crea un socket tipo PULL, para recibir mensajes
context = ZMQ::Context.new(1)
pull = context.socket(ZMQ::PULL)
# Escucha (recibe mensajes de cualquier IP) en el puerto 5567
pull.bind("tcp://*:5567")

puts "SERVIDOR DE COMANDAS EN FUNCIONAMIENTO"
while true
      mensaje = ''
      pull.recv_string(mensaje)
      
      comanda = JSON.parse(mensaje)
      puts "COMANDA #{comanda['folio']} recibida a las: "+Time.now.strftime("%H:%M:%S")
      puts "Mesa: #{comanda['mesa']}, Mesero: #{comanda['mesero']}"
      puts "="*50
      puts " "
      
      comanda["orden"].each do |partida|
         puts "#{partida['cantidad']} #{partida['descripcion']}"
      end
      puts "*"*50
      puts " "
end

Programa creaComandas.rb

#!/usr/bin/ruby
#encoding: utf-8

require 'ffi-rzmq'
require 'json'

# Esta función regresa verdadero si el dato que recibe es un número
# y regresa falso en caso contrario
def numero_valido?(dato)
    true if Float(dato) rescue false
end

# Crea un socket tipo PUSH, para enviar mensajes
context = ZMQ::Context.new(1)
socket = context.socket(ZMQ::PUSH)
# Se conecta al servidor en el puerto 5567
socket.connect("tcp://192.168.1.1:5567")

folio = 0

loop do
     folio += 1
     puts "COMANDA #{folio}"
     print "Mesa: "
     # Pide al usuario que teclee la mesa
     mesa = gets.chomp
     
     # Si el usuario teclea enter, sale del ciclo loop do (termina el programa)
     if mesa.empty?
        break
     else
         print "Mesero: "
         # Pide al usuario que teclee el mesero
         mesero = gets.chomp
         puts "\nOrden"
         puts "="*50
         orden = []
         
         # Inicia un ciclo loop do para ir capturando la orden (cantidad y descripción)
         loop do
            cantidad = nil
            # Mientras el usuario no teclee un número mayor o igual a cero,
            # el programa le pide que teclee la cantidad
            while true
                  print "Cantidad: "
                  cantidad = gets.chomp
                  # Si el usuario teclea enter, sale del ciclo while
                  break if cantidad.empty?
                  
                  # Si el usuario tecleó un número, sólo sale del ciclo while si el número
                  # es mayor o igual a cero
                  # Si el usuario no tecleó un número, el ciclo while continúa ejecutándose
                  if numero_valido?(cantidad)
                     break if cantidad.to_i>=0
                  end
            end
            # Si el usuario tecleó cero para el campo cantidad, significa que terminó
            # de capturar la orden; por lo tanto se sale del ciclo loop do
            
            break if cantidad.to_i == 0
            # Como el usuario no tecleó cero para el campo cantidad, le pide que teclee la descripción
            print "Descripción: "
            descripcion = gets.chomp
            # Agrega al arreglo orden la cantidad y descripción tecleadas por el usuario
            orden.push({:cantidad => cantidad, :descripcion => descripcion})
         end
         
         comanda = {:folio => folio, :mesa => mesa, :mesero => mesero,
                    :orden => orden}.to_json
         
         # Manda los datos de la comanda al servidor
         socket.send_string(comanda)
     end
end

 

Instalación del editor de textos Atom en Debian Stretch

septiembre 26, 2018

En los repositorios de la versión 9 de Debian (Stretch) no viene el editor de textos Atom. Sin embargo, los desarrolladores del editor publicaron repositorios oficiales para diferentes distribuciones de GNU/Linux.

Para todas las distribuciones que usan el sistema de paquetes .deb, sólo publicaron el repositorio para las versiones de 64 bits.

Antes de añadir el repositorio al archivo /etc/apt/sources.list, debemos agregar la llave gpg para que no marque error al hacer el apt update.

curl -L https://packagecloud.io/AtomEditor/atom/gpgkey | sudo apt-key add -

Abrimos el archivo /etc/apt/sources.list y agregamos el repositorio

deb [arch=amd64] https://packagecloud.io/AtomEditor/atom/any/ any main

Como el repositorio está en un URL con SSL (https) debemos instalar el paquete apt-transport-https

sudo apt install apt-transport-https

A continuación ejecutamos sudo apt update y finalmente instalamos el editor de textos Atom con sudo apt install atom

Crédito: Basado en el post de https://www.linuxuprising.com/2018/05/official-atom-repository-for-ubuntu.html

Ordenar un arreglo de objetos con base en un campo específico en Ruby

julio 3, 2018

Decidí hacer este post a raíz de algo que tuve que hacer cuando desarrollé el sistema Clepsidra. Lo que aquí muestro no es exactamente lo mismo, aquí está el concepto básico esperando que pueda servir para solucionar casos similares que se le puedan presentar a alguien más.

Supongamos que tenemos la siguiente información en formato json:

'[
  {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
  {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
  {"clave_asignatura":"DB-101","nombre_asignatura":"Introducción a las bases de datos relacionales","clave_profesor":"P205","nombre_profesor":"Edgar Frank Codd"},
  {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
  {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P235","nombre_profesor":"Brian Kernighan"},
  {"clave_asignatura":"SL-101","nombre_asignatura":"Introducción al software libre","clave_profesor":"P215","nombre_profesor":"Richard M. Stallman"},
  {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P230","nombre_profesor":"Ken Thompson"},
  {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P220","nombre_profesor":"Andrew S. Tanenbaum"},
  {"clave_asignatura":"L-101","nombre_asignatura":"Linux básico","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
  {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P230","nombre_profesor":"Ken Thompson"}
 ]'

Son 10 registros, cada uno con 4 campos: clave_asignatura, nombre_asignatura, clave_profesor y nombre_profesor.

Esta información en formato json la pudimos haber obtenido desde un archivo o desde una base de datos, etc. eso es irrelevante. Como se puede observar, hay asignaturas que aparecen más de una vez, ya que una misma asignatura puede ser impartida por diferentes profesores, y debido a que un profesor puede impartir diferentes asignaturas, también hay profesores que aparecen más de una vez.

Supongamos que a partir de esta información tenemos que obtener una lista de las diferentes asignaturas ordenada por clave de asignatura. Es decir, en la lista ordenada, cada asignatura debe aparecer sólo una vez.

Lo primero que tenemos que hacer es convertir la información a un arreglo de hashes.

@json_data = '[
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
               {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
               {"clave_asignatura":"DB-101","nombre_asignatura":"Introducción a las bases de datos relacionales","clave_profesor":"P205","nombre_profesor":"Edgar Frank Codd"},
               {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P235","nombre_profesor":"Brian Kernighan"},
               {"clave_asignatura":"SL-101","nombre_asignatura":"Introducción al software libre","clave_profesor":"P215","nombre_profesor":"Richard M. Stallman"},
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P230","nombre_profesor":"Ken Thompson"},
               {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P220","nombre_profesor":"Andrew S. Tanenbaum"},
               {"clave_asignatura":"L-101","nombre_asignatura":"Linux básico","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
               {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P230","nombre_profesor":"Ken Thompson"}
              ]'

@materias_profesores = JSON.parse(@json_data)

En el código anterior, la información en formato json la tenemos en la variable @json_data y ya como un arreglo de hashes la tenemos en la variable @materias_profesores

Si lo deseamos, podemos recorrer nuestro arreglo de hashes con el siguiente código para mostrar la información

@materias_profesores.each do |registro|
   puts registro["clave_asignatura"]+": "+registro["nombre_asignatura"]
   puts registro["clave_profesor"]+": "+registro["nombre_profesor"]
end

Para obtener la lista de asignaturas sin repetir lo que vamos a hacer es recorrer nuestro arreglo de hashes e ir agragando la clave de asignatura y su nombre a otro arreglo. Este nuevo arreglo será un arreglo de objetos en donde cada objeto sólo tendra dos campos, la clave de asignatura y el nombre de asignatura. Así que creamos la siguiente clase

class Materia
   attr_accessor :clave_asignatura, :nombre_asignatura
   
   def initialize(clave, nombre)
       @clave_asignatura = clave
       @nombre_asignatura = nombre
   end
end

Crearemos un arreglo llamado @asignaturas, en éste arreglo iremos agregando objetos de la clase Materia.

Como ya comenté antes, recorreremos nuestro arreglo @materias_profesores y antes de crear e insertar un objeto de la clase Materia a nuestro arreglo llamado @asignaturas, verificamos que no exista ya un objeto con esa información, así garantizamos que cada asignatura sólo aparezca una vez.

@asignaturas = []
@materias_profesores.each do |registro|
    # Sólo agrega la asignatura si no se encuentra ya en el arreglo @asignaturas
    if @asignaturas.all? {|asignatura| asignatura.clave_asignatura != registro["clave_asignatura"]}
       @asignaturas.push(Materia.new(registro["clave_asignatura"],registro["nombre_asignatura"]))
    end
end

Hasta ahora ya tenemos un arreglo de objetos de la clase Materia (nuestro arreglo llamado @asignaturas) en donde existe un objeto por cada una de las diferentes asignaturas.

Podemos usar el siguiente código si queremos mostrar el contenido de nuestro arreglo @asignaturas

puts "\n"
puts "="*20
puts "ASIGNATURAS"
puts "="*20
@asignaturas.each do |asignatura|
   puts asignatura.clave_asignatura+": "+asignatura.nombre_asignatura
end

Ahora sólo falta ordenar nuestro arreglo de objetos por el campo clave_asignatura

@asignaturas.sort! {|a, b| a.clave_asignatura <=> b.clave_asignatura}

Programa completo

#encoding: utf-8
require 'json'

class Materia
   attr_accessor :clave_asignatura, :nombre_asignatura
   
   def initialize(clave, nombre)
       @clave_asignatura = clave
       @nombre_asignatura = nombre
   end
end

@json_data = '[
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
               {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P210","nombre_profesor":"Dennis Ritchie"},
               {"clave_asignatura":"DB-101","nombre_asignatura":"Introducción a las bases de datos relacionales","clave_profesor":"P205","nombre_profesor":"Edgar Frank Codd"},
               {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P235","nombre_profesor":"Brian Kernighan"},
               {"clave_asignatura":"SL-101","nombre_asignatura":"Introducción al software libre","clave_profesor":"P215","nombre_profesor":"Richard M. Stallman"},
               {"clave_asignatura":"C-101","nombre_asignatura":"Introducción al lenguaje de programción C","clave_profesor":"P230","nombre_profesor":"Ken Thompson"},
               {"clave_asignatura":"SO-101","nombre_asignatura":"Sistemas operativos","clave_profesor":"P220","nombre_profesor":"Andrew S. Tanenbaum"},
               {"clave_asignatura":"L-101","nombre_asignatura":"Linux básico","clave_profesor":"P225","nombre_profesor":"Linus Torvalds"},
               {"clave_asignatura":"U-101","nombre_asignatura":"Unix avanzado","clave_profesor":"P230","nombre_profesor":"Ken Thompson"}
              ]'

@materias_profesores = JSON.parse(@json_data)

@materias_profesores.each do |registro|
   puts registro["clave_asignatura"]+": "+registro["nombre_asignatura"]
   puts registro["clave_profesor"]+": "+registro["nombre_profesor"]
end

@asignaturas = []
@materias_profesores.each do |registro|
   # Sólo agrega la asignatura si no se encuentra ya en el arreglo @asignaturas
   if @asignaturas.all? {|asignatura| asignatura.clave_asignatura != registro["clave_asignatura"]}
      @asignaturas.push(Materia.new(registro["clave_asignatura"],registro["nombre_asignatura"]))
   end
end

puts "\n"
puts "="*20
puts "ASIGNATURAS"
puts "="*20
@asignaturas.each do |asignatura|
   puts asignatura.clave_asignatura+": "+asignatura.nombre_asignatura
end

# Ordena el arreglo @asignaturas por el campo clave_asignatura
@asignaturas.sort! {|a, b| a.clave_asignatura <=> b.clave_asignatura}

puts "\n"
puts "="*20
puts "ASIGNATURAS ORDENADAS POR CLAVE"
puts "="*20
@asignaturas.each do |asignatura|
   puts asignatura.clave_asignatura+": "+asignatura.nombre_asignatura
end

Quien quiera practicar, puede hacer una versión de este programa en donde utilizando la misma información en formato json, obtenga una lista de los diferentes profesores ordenada por el campo nombre_profesor

 

Creación de un calendario en HTML usando Ruby

junio 22, 2018

En este post voy a mostrar un programa que desarrollé en Ruby que recibe por teclado un año y genera un archivo html que contiene el calendario correspondiente a dicho año.

Saber si un año es bisiesto

Para generar el calendario necesitamos saber si el año en cuestión es bisiesto o no para tomar en cuenta 28 o 29 días en el mes de Febrero.

Hay dos casos en los que un año es bisiesto:

  1. Si es divisible entre 4 pero no entre 100 (es decir, si dividimos el año entre 4 y el residuo es cero, pero si lo dividimos entre 100 y el residuo no es cero)
  2. Si es divisible entre 400

La siguiente función recibe como parámetro un año; regresa verdadero si dicho año es bisiesto y regresa falso en caso contrario.

def Bisiesto(year)
    es_bisiesto = nil
    
    if (year%4 == 0 and year%100 !=0) or year%400 == 0
        es_bisiesto = true
    else
        es_bisiesto = false
    end
    
    return es_bisiesto
end

A continuación muestro una función que recibe como parámetro un año y un número de mes (considerando Enero = 1, Febrero = 2, etc.) y regresa el número de días que contiene ese mes en ese año. Cuando el mes es Febrero, utiliza la función Bisiesto descrita anteriormente para saber si regresa 28 o 29.

def DiasMes(mes, year)
    dias = 30
    
    case mes
         when 1,3,5,7,8,10,12 then dias = 31
         when 2 then
                if Bisiesto(year)
                   dias = 29
                else
                    dias = 28
                end
    end

    return dias
end

Saber que día de la semana es una fecha específica

Con las dos funciones descritas hasta ahora, ya podemos crear un programa que solicite al usuario que teclee un año y mediante un ciclo del 1 al 12 (los meses del año) obtener el número de días que contiene cada mes en ese año (realmente para cualquier año el único mes que puede tener diferentes días es Febrero dependiendo de si es bisiesto o no). Pero necesitamos saber que día de la semana es el primer día de cada mes para ese año; para eso haremos un función en donde implementaremos la regla de Zeller.

Para saber más sobre los casos en que se da un año bisiesto y sobre la regla de Zeller les recomiendo consultar este enlace The Calendar and the Days of the Week

La función recibe como parámetros un número de día, número de mes y un año. Utilizando la regla de Zeller regresa el número del día de la semana empezando con Domingo = 0

def DiaSemana(dia, mes, year)   
    ajuste = (14-mes)/12
    mm = mes+(12*ajuste)-2
    yy = year-ajuste
    
    return (dia+(((13*mm)-1)/5)+yy+(yy/4)-(yy/100)+(yy/400))%7
end

Si a la función DiaSemana le paso como parámetros la fecha de hoy (22, 6, 2018) me regresa el número 5, lo que significa que el 22 de Junio del año 2018 es Viernes.

El archivo html que generará nuestro programa contendrá una tabla para cada uno de los meses; para tener como encabezado de cada tabla el nombre del mes, haremos una función más que reciba como parámetro el número del mes y regrese el nombre considerando 1 = Enero, 2 = Febrero, etc.

Convertir un número de mes a su nombre correspondiente

def MesNumero(numero)
    mes = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"]
    
    if (1..12).include?(numero)
        return mes[numero-1]
    else
        return "Número inválido"
    end
end

Programa completo

Con eso tenemos todas las funciones que necesitamos. A continuación muestro el programa completo. Después de las funciones está el código en donde se pide al usuario que teclee un año y después genera el archivo html que contendrá el calendario correspondiente a ese año.

Programa calendar.rb

#encoding: utf-8

# Recibe como parámetro un año
# Regresa verdadero si dicho año es bisiesto y regresa falso en caso contrario
# Hay dos casos en que un año es bisiesto:
# caso 1: Si es divisible entre 4 pero no entre 100
# caso 2: Si es divisible entre 400
def Bisiesto(year)
    es_bisiesto = nil

    if (year%4 == 0 and year%100 !=0) or year%400 == 0
        es_bisiesto = true
    else
        es_bisiesto = false
    end
    
    return es_bisiesto
end

# Recibe como parámetros un mes y año
# Regresa el número de días que contiene ese mes en ese año tomando en cuenta si es año bisiesto o no
def DiasMes(mes, year)
    dias = 30
    
    case mes
         when 1,3,5,7,8,10,12 then dias = 31
         when 2 then
              if Bisiesto(year)
                 dias = 29
              else
                  dias = 28
              end
         end
    
    return dias
end

# Recibe como parámetros un número
# Regresa el nombre del mes correspondiente al número que recibe (1 = Enero, 2 = Febrero, etc.)
def MesNumero(numero)
    mes = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"]
    
    if (1..12).include?(numero)
        return mes[numero-1]
    else
        return "Número inválido"
    end
end

# Recibe como parámetros un día, mes y año
# Utiliza la regla de Zeller para regresar el número del día de la semana empezando con Domingo = 0
def DiaSemana(dia, mes, year)
    ajuste = (14-mes)/12
    mm = mes+(12*ajuste)-2
    yy = year-ajuste
    
    return (dia+(((13*mm)-1)/5)+yy+(yy/4)-(yy/100)+(yy/400))%7
end

puts "Año:"
anio = gets.chomp.to_i

File.open('calendario'+anio.to_s+'.html','w') do |linea|
     dia_semana = nil
     dias_mes = nil
     
     linea.puts '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
     linea.puts '<html xmlns="http://www.w3.org/1999/xhtml">'
     linea.puts '<head>'
     linea.puts '<title>Calendario</title>'
     linea.puts '</head>'
     linea.puts '<body>'
     linea.puts '<h2>Calendario '+anio.to_s+'</h2>'
     (1..12).each do |mes|
             dia_semana = DiaSemana(1,mes,anio)
             dias_mes = DiasMes(mes, anio)
             
             linea.puts ' <table>'
             linea.puts ' <thead>'
             linea.puts ' <tr>'
             linea.puts ' <th colspan="7">'+MesNumero(mes)+'</th>'
             linea.puts ' </tr>'
             linea.puts ' <tr>'
             linea.puts ' <th>D</th>'
             linea.puts ' <th>L</th>'
             linea.puts ' <th>M</th>'
             linea.puts ' <th>M</th>'
             linea.puts ' <th>J</th>'
             linea.puts ' <th>V</th>'
             linea.puts ' <th>S</th>'
             linea.puts ' </tr>'
             linea.puts ' </thead>'
             linea.puts ' <tbody>'
             linea.puts ' <tr>'
             if dia_semana > 0
                for i in 1..dia_semana
                    linea.puts ' <td></td>'
                end
             end
             j = dia_semana
             for dia in 1..dias_mes
                 linea.puts ' <td style="text-align: right;">'+dia.to_s+'</td>'
                 j += 1
                 if j == 7
                    linea.puts ' </tr>'
                    j = 0
                    if dia < dias_mes
                       linea.puts ' <tr>'
                    end
                 end
             end
             if j != 0
                linea.puts ' </tr>'
             end
             linea.puts ' </tbody>'
             linea.puts ' </table>'
             linea.puts ' <br>'
     end
     linea.puts '</body>'
     linea.puts '</html>'
end

puts "\nSe generó el archivo calendario#{anio}.html"

 

El operador condicional en C

junio 20, 2018

El operador condicional ?: tiene tres operandos, lo que significa que es un operador ternario.

La forma general de éste operador es:

expresion1 ? expresion2 : expresion3

Funciona de la siguiente forma:

Se evalúa expresion1, si el resultado de la evaluación es verdadero (diferente de cero), se ejecuta expresion2; si el resultado de la evaluación de expresion1 es falso (igual a cero), se ejecuta expresion3.

Ejemplo:

printf("%s\n", calificacion >=70 ? "Aprobado" : "Reprobado");

En la instrucción anterior, si el valor de la variable calificacion es mayor o igual a 70, se imprime la cadena “Aprobado”, en caso contrario se imprime la cadena “Reprobado”.

El siguiente programa muestra otro ejemplo del uso del operador condicional

#include <stdio.h>
int main()
{
 int num1=0, num2=0, mayor=0;
 
 printf("Introduzca dos números enteros\n");
 scanf("%d%d",&num1,&num2);
 mayor = (num1 >= num2 ? num1 : num2);
 printf("El número mayor es %d\n",mayor);
 
 return 0;
}

 

FreeBSD como servidor web en una PC con pocos recursos

abril 20, 2018

Dentro de la gran cantidad de software libre que existe hoy en día, se encuentran algunos sistemas operativos bastante eficientes y poderosos como los de la familia BSD (FreeBSD, OpenBSD, NetBSD, etc.) y las distribuciones GNU/Linux. Es bien sabido que con este tipo de sistemas operativos podemos aprovechar computadoras con bajas prestaciones en las cuales los sistemas operativos privativos más conocidos no pueden instalarse.

Muchos creen que el único uso que se le podría dar a una PC con procesador Pentium 4 o inferior sería como firewall, router, o algo parecido.

Aquí les dejo un video en donde muestro una PC con procesador Pentium II a 400 Mhz con sólo 128 Mb de RAM en la que instalé FreeBSD 11.1 (la última versión al momento), nginx, y el sistema ANI (Administración de Negocios Inteligente).