Variable not found
Blazor Server-Side Rendering en .NET 8
noviembre 28, 2023 07:05

Como sabéis, hasta ahora, los componentes Blazor podían ejecutarse en dos tipos de hosting distintos, Blazor Server y Blazor WebAssembly. Aunque cada uno tiene sus escenarios ideales de uso, ambos enfoques conseguían lo mismo, implementar Single Page Applications en C#, sin necesidad de utilizar JavaScript (bueno, o al menos, minimizando radicalmente su uso):
- Con Blazor Server, se mantiene en el servidor una copia del DOM de cada usuario conectado, de forma que C# puede manipularlo directamente. Luego, mediante una conexión websockets, se sincronizan los cambios con el cliente.
- Blazor WebAssembly lleva al navegador el runtime de .NET, las bibliotecas base y los ensamblados de la aplicación, por lo que todo se ejecuta directamente en el lado cliente gracias a WebAssembly.
Estas dos formas de ejecutar componentes son muy apropiadas cuando se trata de elementos interactivos, capaces de responder a eventos de usuarios y con capacidad de actualizar el contenido de la página. Sin embargo, son muy poco eficientes cuando se trata de páginas estáticas que no requieren interacción con el usuario:
-
Para poder mostrar en el navegador un contenido, en Blazor Server hay que esperar a que el lado cliente descargue la página contenedora, un archivo JavaScript y se establezca la conexión websockets con el servidor, tras lo cual se enviará el contenido de la página.
-
En Blazor WebAssembly, el cliente debe descargar la página contenedora, los ensamblados y todo el entorno de ejecución de .NET y lanzarlo todo sobre WebAssembly. En este momento se puede mostrar el contenido de la página.
Con ASP.NET Core 8, Blazor añade un tercer tipo de hosting, Server-Side Rendering, que permite a las páginas de Blazor recibir directamente peticiones HTTP, renderizar y retornar el contenido estático al navegador. En este caso, no es necesario establecer una conexión websockets, ni descargar el runtime de .NET, porque lo que se envía al lado cliente es HTML puro, el resultante de renderizar los componentes Razor en el servidor. Vaya, algo muy similar a lo que ocurre cuando accedemos a páginas generadas con ASP.NET Core MVC, Razor Pages o cualquier otro framework de backend.
Es decir, en la práctica, se trata de un mecanismo que nos permite generar páginas completas usando la fantástica sintaxis, potencia y productividad del modelo de componentes de Blazor.
Con ello conseguimos un objetivo muy deseado: hacer de Blazor un framework de desarrollo web fullstack, que nos permitirá implementar cualquier tipo de aplicación web, desde SPAs ricas, dinámicas e interactivas, hasta sitios web eminentemente estáticos, como páginas corporativas, blogs, foros, etc., donde no es necesaria una interacción tan cercana al usuario.
Obviamente, si usamos Server-Side Rendering no podemos manipular el DOM ni implementar ningún tipo de comportamiento interactivo. Por ejemplo, imagina una página Blazor como la siguiente:
@page "/counter"
<p>Current: @count</p>
<button @onclick="()=>count++">Click!</button>
@code {
int count = 0;
}
Esta página será mostrada correctamente en el navegador con el valor inicial de counter
y un botón, pero no responderá al evento onclick
. Blazor SSR simplemente se encarga de retornar muy rápidamente al navegador contenido estático, que además será fácil de indexar por parte de los buscadores y otros bichos que rastrean la web.
Adicionalmente, en Blazor SSR se han incluido algunas mejoras progresivas para hacer la experiencia de usuario más fluida durante su navegación por las páginas y en el uso de formularios, pero lo veremos en otro post más adelante.
Si necesitamos componentes interactivos, podemos combinar Blazor Server-Side Rendering con Blazor Server o Blazor WebAssembly de forma muy sencilla, porque estaremos hablando en todo momento el mismo idioma: al final todo son componentes Razor. Simplemente, algunos de ellos se ejecutarán de forma estática en el servidor, y otros en el lado cliente (usando el hosting WebAssembly o Server según las necesidades concretas de cada caso).
Por ejemplo, si queremos que en una página renderizada con Blazor SSR se incluya un componente interactivo con renderizado en servidor (Blazor Server), bastará con indicarlo en el momento de su instanciación mediante el atributo @rendermode
:
@page "/"
<p>
Este texto se renderiza estáticamente (SSR),
pero lo siguiente es un componente interactivo.
</p>
<Counter @rendermode="InteractiveServer"></Counter>
De la misma forma, podemos indicar que un componente sea ejecutado en el lado cliente (Blazor WebAssembly), o incluso que sea el propio sistema el que decida si llevarlo al cliente o renderizarlo en el servidor, la opción que sea más rápida en cada momento.
Estas opciones requieren configuraciones adicionales en el
Program.cs
y tener en cuenta algunos detalles más.
Primer vistazo a una aplicación Blazor SSR simple
Veamos un ejemplo de la pinta que tiene todo esto a nivel de código. Para ello, vamos a crear una Aplicación Blazor Server-Side Rendering desde cero, usando la línea de comandos (obviamente, necesitaremos tener instalado el SDK de .NET 8):
D:\MyFirstSSRApp>dotnet new blazor --interactivity none
The template "Blazor Web App" was created successfully.
This template contains technologies from parties other than Microsoft,
see https://aka.ms/aspnetcore/8.0-third-party-notices for details.
Processing post-creation actions...
Restoring D:\MyFirstSSRApp\MyFirstSSRApp.csproj:
Determining projects to restore...
Restored D:\MyFirstSSRApp\MyFirstSSRApp.csproj (in 118 ms).
Restore succeeded.
D:\MyFirstSSRApp>_
También podemos hacerlo desde Visual Studio, usando la nueva plantilla "Blazor Web App" e indicando que no deseamos incluir componentes interactivos.
Si acudimos al archivo Program.cs
, veremos que el código de inicialización es muy parecido al de cualquier otra aplicación ASP.NET Core (de hecho, porque es una aplicación ASP.NET Core 😉). Las únicas novedades destacables las encontramos en las llamadas a AddRazorComponents()
y MapRazorComponents<App>()
:
using MyFirstSSRApp.Components;
var builder = WebApplication.CreateBuilder(args);
// Novedad 1:
builder.Services.AddRazorComponents();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
// Novedad 2:
app.MapRazorComponents<App>();
app.Run();
Como podréis intuir, con AddRazorComponents()
registramos en el inyector de dependencias los servicios necesarios para poder renderizar componentes Razor en el servidor.
Más abajo, con MapRazorComponents<App>()
se descubren los componentes disponibles en la aplicación, y se especifica el componente raíz, que en este caso es <App>
. Observad que, a diferencia de los otros hostings de Blazor (Server y WebAssembly), no hace falta una página que actúe como host para indicar el componente raíz de la aplicación, ya lo estamos haciendo en la propia de inicialización.
El código del componente <App>
, que hemos indicado que es el componente raíz de la aplicación, está disponible en el archivo App.razor
, e incluye el esqueleto de la página HTML que se enviará al navegador además de:
- El clásico componente
<HeadOutlet>
, que nos permitirá introducir contenido en el encabezado - El componente
<Routes>
que cargará el sistema de routing y actuará como placeholder de los componentes que irá cargando el sistema de routing de Blazor.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="MyFirstSSRApp.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
El componente <Routes>
es el encargado de cargar los componentes Razor de tipo página cuya ruta corresponda a la solicitada en la petición:
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
</Router>
A partir de aquí, todo son las páginas y componentes Razor habituales. Por ejemplo, la página de inicio está definida en el componente Home.razor
de la siguiente forma:
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
Como podéis ver, salvo el código de inicialización, ¡son todo componentes Razor!
En definitiva...
En este post hemos echado un vistazo de alto nivel a Blazor Server-Side Rendering, el nuevo modelo de renderizado de componentes Razor destinado a convertir el framework en una verdadera solución fullstack.
Obviamente hay mucho más de lo que hemos visto aquí, pero creo que de momento es suficiente para al menos saber de qué va y qué podemos esperar de esta interesante novedad de la la versión más reciente de Blazor.
Publicado en: www.variablenotfound.com.Variable not found
Enlaces interesantes 546
noviembre 27, 2023 07:05

Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Declaración de espacios de nombre en una línea con file-scoped namespaces de C#10
José María Aguilar - Establecer el foco automáticamente en MVC, versión declarativa
José María Aguilar
.NET Core / .NET
- .NET 8: Todas las novedades en 2 minutos
José Manuel Alarcón - Announcing .NET Chiseled Containers
Richard Lander - ConcurrentStack in C#
Code Maze - Find a subset from a set of values whose sum is closest to a specific value–C#
Bart Wullems - Intro to .NET and C# - Differences, Structure, SDK, Runtime...
Code Maze - Embedding additional files in an MSBuild binary log file
Gérald Barré - A failed experiment with interceptors in C# 12 and .NET 8
Andrew Lock - Lo-Fi Service Discovery in .NET8
David Whitney - Fake It Til You Make It...To Production
Martin Taillefer - Validating JSON Against a Schema
Paul Michaels - The Differences Between Quartz.NET and Hangfire
Code Maze - Async Lambda Statements
Bryan Hogan - Primary Constructors – Using C# 12 in Rider and ReSharper
Matthias Koch - How To Harness System.Reactive For The Observer Pattern
Nick Cosentino
ASP.NET Core / ASP.NET / Blazor
- Improve ASP.NET Core authentication using OAuth PAR and OpenID Connect
Damien Bowden - .NET Blazor
Dustin Moris - ASP.NET Core PDF Previews and Downloads
Khalid Abuhakmeh - Uploading Files with Telerik UI for Blazor
Jefferson S. Motta - Blazor .NET 8 - Enhanced Form Navigation
Steven Giesel - ASP.NET Core Basics: Essential NuGet Packages (Part 1)
Assis Zang - Exploring what is inside the ASP.NET Core cookies
Tore Nestenius - Syncfusion Blazor Components Are Compatible with .NET 8.0
Rajendran R. - Cookies, Tokens, or JWTs? The ASP.NET Core Identity Dilemma
Andrea Chiarelli - How to include an Angular Project within .NET Aspire
Tim Deschryver
Azure / Cloud
- Evolving Core Stack of Azure SDK for JS: A Tale of Enhanced Performance, Usability, and Convenience
Harsha Nalluru - Creating Zip Files in Azure Blob Storage
Mark Heath
Conceptos / Patrones / Buenas prácticas
- C# Design Patterns: A Practical Guide
Darren Horrocks - Trimming a Fake Object
Mark Seemann - gRPC vs. REST
The Postman Team
Data
- How To Use EF Core Interceptors
Milan Jovanović - Strategies for queries against bit columns
Aaron Bertrand
Machine learning / IA / Bots
- Introducing Claude 2.1
Anthropic
Web / HTML / CSS / Javascript
- Announcing TypeScript 5.3
Daniel Rosenwasser - 6 Basic Tips For Optimizing React Performance
Sufian mustafa - React Basics: Guide to the useEffect Hook
John Au-Yeung - Easily Create Interactive Digital Logic Circuits in Angular
Vivisa Ganesan - Creating a React App From Scratch
Magnus Montin - Higher Order Functions in JavaScript
Esther Vaati - CSS trick: transition from height 0 to auto!
Francesco Vetere - A Few Ways CSS Is Easier To Write In 2023
Geoff Graham - Easily Draw any Polygon
Kirupa Chinnathambi
Visual Studio / Complementos / Herramientas
- Announcing NuGet.exe and NuGet Client SDK Packages Support Policy: Keeping You Informed and Secure
Kartheek Penagamuri - Create a Pull Request with Summary Difference View Available in GA
Jessie Houghton - Trying to automate Microsoft Entra ID App Registration process using Terraform
Carlos Pons - Building debugging context for Copilot Chat
Mark Downie - Visualize Entity Framework Relationships and Additional Query Analysis in ReSharper 2023.3
Maarten Balliauw - Validate JSON files against schema in Azure DevOps build
Thomas Ardal - GitHub Codespaces: A Faster Way to Develop in the Cloud & Set up GitHub Codespaces for a .NET 8 application
Emanuele Bartolesi - Test-Driving Windows 11 Dev Drive for .NET
Maarten Balliauw - Visual Studio 2022 – 17.8 Performance Enhancements
Nayana Srikanth - This looks like a NuGet bug
Matt Lacey
.NET MAUI / Xamarin / Mobile
- Announcing .NET 8 Support for Syncfusion .NET MAUI Controls!
Paul Anderson - Improvements & Changes in Android resource generation in .NET 8
Dean Ellis - Displaying HTML in a .NET MAUI Label
Leomaris Reyes
Otros
- Cl@ve, Certificado, DNIe...: Todas las formas de acceder a la Administración, explicadas
José Manuel Alarcón
Publicado en Variable not found.
Variable not found
Enlaces interesantes 545
noviembre 20, 2023 07:05

Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Curiosidad: ¿Por qué el encabezado 'referer' se llama así, y cómo nos afecta a los desarrolladores .NET?
José María Aguilar - ASP.NET MVC: establecer el foco en un control al cargar la página
José María Aguilar
.NET Core / .NET
- Announcing .NET 8
Gaurav Seth - Announcing C# 12
Kathleen Dollard - Announcing F#
Tomáš Grošup - Removing allocations by reducing closure scopes using local variables
Gérald Barré - The Evolution of C#
Aram Tchekrekjian - Optimizing C# code analysis for quicker .NET compilation
Anthony Simmon - Exploring What's New in C# 12
Darren Horrocks - VestPocket: File-based Data Storage for AOT .NET Applications
Khalid Abuhakmeh - How to Use Interceptors in C# 12
Code Maze - A more flexible and enhanced way of logging in .NET 8
Steven Giesel - How to extract, create, and navigate Zip Files in C#
Davide Bellone - C# Interceptors
Paul Michaels - Enums in CSharp – A Simple Guide To Expressive Code
Nick Cosentino - Alias any Type with C# 12
Michael Jolley - Top 10 Must-Have Features in Syncfusion’s C# PDF Library
Sowmiya Loganathan - Standard and Custom Numeric Format Strings in .NET
Code Maze - The CSharp Switch Statement – How To Go From Zero To Hero
Nick Cosentino
ASP.NET Core / ASP.NET / Blazor
- Announcing ASP.NET Core in .NET 8
Daniel Roth - Authentication with multiple identity providers in ASP.NET Core
Damien Bowden - Global Exception Handling in ASP.NET WEB API
Abdul Rahman Shabeek Mohamed - Test Driven Development with Blazor - Webinar Recording
Scott Sauber - How To Easily Create PDF Documents in ASP.NET Core
Milan Jovanović - Boost Blazor Dropdown List Performance with Virtualization
Saravanan G - Telerik UI for Blazor and ASP.NET Core in .NET 8 and Beyond
Lyubomir Atanasov - Automatically Analyze ASP.NET Core Performance With Dynamic Program Analysis
Maarten Balliauw
Azure / Cloud
- Introducing .NET Aspire: Simplifying Cloud-Native Development with .NET 8
Glenn Condron - .NET 8 GA available on App Service
Tulika Chaudharie - Power what’s next with limitless relational databases from Azure
Shireesh Thota
Conceptos / Patrones / Buenas prácticas
- Lo que no se mide, no se puede mejorar. Lo que no se mejora, se degrada.
Juanma Laó - Pipes and Filters Architectural Pattern in C#
Code Maze - Fakes are Test Doubles with contracts
Mark Seemann - What are Business Rules? It's not this.
Derek Comartin - The Hidden Dangers of Over-Commenting in Code
Michal Zakrewski
Data
- Entity Framework Core 8 (EF8) is available today
Arthur Vickers - The Current Status of EF Core
Ricardo Peres
Machine learning / IA / Bots
- GhostBuster (& DetectGPT + GPTZero) para detectar un LLM usado como GhostWriter... o no.
Chema Alonso - Introducing v1.0.0 Beta6 for the .NET Semantic Kernel SDK
Matthew Bolanos - Build a chatbot with the new OpenAI Assistant API and Function Calling
Fernando González Tostado - Reverse Engineering a Neural Network's Clever Solution to Binary Addition
Casey Primozic - DBSCAN Data Clustering from Scratch Using C#
James McCaffrey - Building ChatGPT-Like Experiences with Azure: A Guide to Retrieval Augmented Generation for JavaScript applications
Hector Norzagaray
Web / HTML / CSS / Javascript
- Angular 17: estas son sus novedades
José Manuel Alarcón - Announcing Vite 5
ViteJs Team - Explicit Resource Management: Exploring JavaScript's and TypeScript's new feature
Ilia Pozdnyakov - Mastering Custom React Hooks: Build Your Own Keyboard Event Listener
Barry Michael Doyle - Designing Web Design Documentation
Frederick O’Brien - Total guide to lazy loading with Angular @defer
Tomas Trajan - Using CSS content-visibility to boost your rendering performance
Rob O'Leary - Maximizing Angular Charts Performance with Lazy Loading
Veerakumar Narayanasamy - CSS Responsive Multi-Line Ribbon Shapes (Part 1)
Temani Afif - Testing Deferrable Views with Angular Testing Library
Tim Deschryver - Automatic picture-in-picture for web apps
François Beaufort
Visual Studio / Complementos / Herramientas
- Visual Studio 17.8 now available!
Adrian Murphy - Introducing Visual Studio 17.9 - Preview 1 is Here!
Maddy Montaquila - The Visual Studio UI Refresh Preview is here!
Dante Gagne - Announcing NuGet 6.8 - Maintaining Security with Ease
Allie Barry
.NET MAUI / Xamarin / Mobile
- Announcing .NET MAUI in .NET 8
David Ortinau - Introducing Native Authentication for consumer mobile applications
Kaushik Kislay - Dealing with application windows on Windows with .NET MAUI
Marco Siccardi - Creating Empty State for you .NET MAUI Apps
Leomaris Reyes - .NET MAUI on Linux with Visual Studio Code
Bruno Capuano
Otros
- BOOK OF NEWS - Microsoft Ignite 2023
Mary Reynolds
Publicado en Variable not found.
Picando Código
Novedades de IRB en Ruby 3.3: Autocompletado basado en tipos, debug y más
noviembre 17, 2023 01:07
Estamos a pocos días del 25 de diciembre, lo que significa versión nueva de Ruby. Ya podemos ir probando Ruby 3.3 con ruby-3.3.0-preview3
. Una de las tantas novedades de esta versión es IRB 1.9, con varias cosas nuevas.
Autocompletado basado en tipos
Por defecto, IRB usa expresiones regulares para el autocompletado con IRB::RegexpCompletor
. El nuevo IRB::TypeCompletion
, usa análisis de tipo con Prism (parser introducido en Ruby 3.3) y RSB (el sistema de tipos presentado en Ruby 3).
Para habilitar IRB::TypeCompletion
, tenemos que ejecutar IRB con el parámetro --type-completor
:
O agregar el siguiente código en nuestro archivo ~/.irbrc
:
Con este autocompletado activado, tenemos la ventaja de contar con autocompletado de métodos encadenados, parámetros de un bloque y más si está disponible la información del tipo. Por ejemplo en un bloque:
Al usar los elementos de un Array como parámetro del bloque enviado a map
, IRB sabe que l
es un String y nos muestra el autocompletado disponible, algo que no funciona con el motor basado en expresiones regulares. Lo mismo aplica a métodos encadenados:
Me pareció una característica bastante interesante. En este enlace podemos ver el Pull Request de esta funcionalidad y estudiar el código. Podemos leer más información en IRB: Typed Based Completion. El autocompletado se puede deshabilitar completamente ejecutando IRB_USE_AUTOCOMPLETE=false irb
, o exportando esa variable de ambiente en alguno de nuestros archivos de perfil.
Integración con debug
Hace poco escribí sobre la gema debug. A partir de IRB 1.8, tenemos integración con dicha gema en IRB. Al agregar un breakpoint en nuestro código con binding.irb
, podemos activar el debugger con el comando debug
y empezar a usar los comandos de esa gema, además de tener habilitados los comandos de IRB:
Cambiar los colores del diálogo de autocompletado
Otra característica nueva es el soporte para cambiar el color del fondo y el estilo de la fuente del diálogo de autocompletado con la class Reline::Face
.
Tenemos que escribir la configuración de colores en nuestro ~/.irbrc
:
Reline::Face.config(:completion_dialog) do |conf| conf.define :default, foreground: :black, background: :bright_green conf.define :enhanced, foreground: :white, background: :magenta conf.define :scrollbar, foreground: :magenta, background: :bright_green end
Más información en su documentación, incluyendo los códigos de colores que podemos usar.
Otras mejoras de IRB en Ruby 3.3
- Soporte de paginador para los comandos
ls
,show_source
yshow_cmds
. - Información más precisa y útil en los comandos
ls
yshow_source
.
Como si fuera poco, IRB ha pasado por un refactorizado (¿refactoreo?) extensivo y recibido muchos arreglos de bugs que facilitarán mejoras a futuro.
Son buenos tiempos para programar en Ruby. Ruby 3.3 va a ser más rápido y más eficiente, como cada versión de Ruby 3 desde 3.0!
El post Novedades de IRB en Ruby 3.3: Autocompletado basado en tipos, debug y más fue publicado originalmente en Picando Código.Picando Código
Disponible Tourist Trap – sorteo de claves de Steam
noviembre 15, 2023 06:03
Hoy se publicó en Steam Tourist Trap (disponible también en XBox), la aventura gráfica surrealista desarrollada en Santa Ballena, una ciudad Sudamericana afectada por la gentrificación turística. En Junio publiqué una entrevista a Manuel González y Juan Andrés Nin, desarrolladores del juego. Fueron tan amables de enviarme una clave Steam para que lo probara y unas más para regalar.
Lo he jugado en Steam con Proton en Linux, y anda bien de bien. Todavía no lo completé, pero hasta ahora me viene gustando mucho. Cumple con la premisa de la descripción cuando habla de “comedia oscura”, la intro tiene un humor característico que le va a gustar a cínicos como uno. No recuerdo la última vez que un juego me hizo largar la carcajada, pero éste lo logró con un desodorante…
Santa Ballena es una ciudad en un mundo distópico con una ambientación así medio como opresiva que caracteriza ese género. Manuel nos contaba en la entrevista que está inspirada en Maldonado y Punta del Este, y se siente muy familiar.
El juego progresa a través de la historia y puzzles, como es característico del género. Encontramos objectos y/o los obtenemos de distintas formas interactuando con gente y el ambiente, y combinando distintos objetos juntos. Algunas veces llegué a un punto que me trancaba. Pero dándole vueltas y pensando en las pistas, eventualmente encontraba cómo seguir. Cuando nos trancamos, la tecla Tab nos muestra “hotspots”, elementos con los que podemos interactuar.
El arte es otro aspecto muy bueno, las escenas como pintadas a mano y el diseño de los personajes caricaturescos y su animación están geniales. La combinación queda muy buena.
Un muy buen juego que recomiendo, ¡mucha suerte a Juan Andrés y Manuel con el lanzamiento!
Sorteo Claves de Steam para Tourist Trap (TERMINADO)
Si te interesa participar por una de las claves Steam de Tourist Trap, podés hacer cualquiera de estas dos cosas:
Dejar un comentario en este post expresando tu interés por participar.Escribir sobre el sorteo en el fediverso (Mastodon, o equivalentes) con la URL de este post y mencionar a @picandocodigo para que pueda ver tu mensaje y agregarte a la lista (o contactame de alguna forma para agregarte al sorteo). Por ejemplo:
Tourist Trap disponible en Steam y XBox. Participá por una clave gratis: https://picandocodigo.net/2023/tourist-trap-steam-sorteo-claves-steam via @picandocodigo
Si dejas comentario y además publicas en el fediverso vale como entrada doble. No hagan trampa, seamos buena gente. Mucha suerte, y publicaré los nombres ganadores este viernes 17 de noviembre.
Ganadores del sorteo: Arlequín y Azel_Dragoon
Les envío la clave de Steam por correo.
Dejo el tráiler más reciente de Tourist Trap:

El post Disponible Tourist Trap – sorteo de claves de Steam fue publicado originalmente en Picando Código.
Variable not found
8 formas de usar tuplas en C#
noviembre 14, 2023 07:05

Desde su llegada con la versión 7 del lenguaje C#, allá por 2017, nuestro lenguaje favorito dispone de soporte para tuplas. Sin embargo, no he visto muchos proyectos donde estén siendo utilizadas de forma habitual; quizás sea porque pueden hacer el código menos legible, o quizás por desconocimiento, o simplemente porque lo que aportan podemos conseguirlo normalmente de otras formas y preferimos hacerlo como siempre para no sorprender al que venga detrás a tocar nuestro código.
Pero bueno, en cualquier caso, es innegable que las tuplas han venido para quedarse, así que en este post vamos a ver algunos usos posibles, y a veces curiosos, de esta característica del lenguaje C#.
1. Realizar asignaciones múltiples de forma más concisa
Las tuplas permiten realizar asignaciones a múltiples variables de forma muy compacta y sencilla. Un caso de uso simple podemos verlo en el siguiente ejemplo, donde usamos una tupla para declarar e inicializar tres variables:
// Esto:
(int i, string s, bool b) = (42, "Hey", true);
Console.WriteLine($"{i}, {s}, {b}"); // 41, Hey, true
// ...es exactamente lo mismo que esto:
var i = 42;
var s = "Hey";
var b = true;
Console.WriteLine($"{i}, {s}, {b}"); // 41, Hey, true
La opción con tuplas no es mucho más concisa que la clásica porque aún no hemos exprimido toda su potencia. Pero podemos reducirla fácilmente:
// Simplificación 1- > Usamos la inferencia de tipos para declarar con "var":
(var i, var s, var b) = (42, "Hey", true);
// Simplificación 2 -> Sacamos de los paréntesis el factor común:
var (i, s, b) = (42, "Hey", true);
También podemos usar tuplas para asignar variables o propiedades existentes, como vemos en el siguiente ejemplo:
int a = 1;
int b = 2;
...
(a, b) = (5, 8);
Console.WriteLine(a + "," + b); // 5,8
2. Intercambio de variables
Como un escenario específico de uso del punto anterior, tenemos el intercambio (swapping) de variables, algo que desde muy pequeñitos nos enseñaron a hacer utilizando una variable de apoyo:
int a = 1, b = 2;
Console.WriteLine("Before: " + a + "-" + b); // Before: 1-2
int c = a;
a = b;
b = c;
Console.WriteLine("After: " + a + "-" + b); // After: 2-1
Las tuplas, gracias a sus posibilidades de asignación múltiple, nos permite realizarlo de un plumazo:
int a = 1, b = 2;
Console.WriteLine("Before: " + a + "-" + b); // Before: 1-2
(a, b) = (b, a);
Console.WriteLine("After: " + a + "-" + b); // After: 2-1
3. Minimización de métodos
También podríamos minimizar o simplificar métodos gracias a la capacidad de asignación múltiple de las tuplas. Por ejemplo, en el siguiente bloque de código podemos ver cómo reducimos el código necesario para implementar el constructor de una clase que lo único que hace es recoger los parámetros entrantes para almacenarlos en propiedades:
class Person
{
public readonly string Name { get; }
public readonly int Age { get; }
public readonly string Email { get; }
public Person(string name, int age, string email) =>
(Name, Age, Email) = (name, age, email);
}
En C# más clásico, el código equivalente sería el siguiente:
class Person
{
public readonly string Name { get; }
public readonly int Age { get; }
public readonly string Email { get; }
public Person(string name, int age, string email)
{
Name = name;
Age = age;
Email = email;
}
}
Personalmente no es que me guste demasiado esta opción, pero bueno, la posibilidad existe y está bien conocerla.
4. Devolver más de un valor desde métodos o funciones
Esta es una de las utilidades más conocidas de las tuplas. Esto nos puede venir bien para evitar los parámetros de salida (out
o ref
) en métodos o funciones, y para ahorrarnos la creación de clases o estructuras de datos creadas exclusivamente para guardar temporalmente los valores retornados.
Veamos un ejemplo muy simple. El siguiente método GetMinAndMax()
retorna los valores mínimos y máximos de un array de enteros, utilizando parámetros out
:
void GetMinAndMax(int[] numbers, out int min, out int max)
{
min = int.MaxValue;
max = int.MinValue;
foreach (int number in numbers)
{
min = Math.Min(min, number);
max = Math.Max(max, number);
}
}
Pero como sabemos, los parámetros out
o ref
son molestos de utilizar y no están disponibles en métodos asíncronos, así que muchas veces optamos por introducir tipos específicos para retornar los valores:
public class MinAndMax
{
public int Min { get; set; }
public int Max { get; set; }
}
MinAndMax GetMinAndMax(int[] numbers)
{
min = int.MaxValue;
max = int.MinValue;
foreach (int number in numbers)
{
min = Math.Min(min, number);
max = Math.Max(max, number);
}
return new MinAndMax() { Min = min, Max = max };
}
Pero vaya pereza, ¿no? Pues en estos casos es donde las tuplas pueden echarnos una mano:
(int Min, int Max) GetMinAndMax(int[] numbers)
{
int min = int.MaxValue;
int max = int.MinValue;
foreach (int number in numbers)
{
min = Math.Min(min, number);
max = Math.Max(max, number);
}
return (min, max);
}
De esta forma, el consumidor del método sería idéntico a si hubiésemos utilizado una clase o estructura para el retorno:
var result = GetMinAndMax(arrayOfIntegers);
Console.WriteLine(result.Min +"-"+ result.Max);
5. Claves múltiples de diccionarios
Otro uso no demasiado conocido de las tuplas es actuar como claves en tipos diccionario, lo cual es interesante cuando queremos tener acceso a los elementos utilizando más de un valor como identificador único.
Este escenario solemos solucionarlo mediante una expresión que de alguna forma concatena los distintos campos que componen la clave, creando un valor único. Por ejemplo, si tuviéramos un diccionario de facturas que usara como clave el año de su emisión y su número de serie, podríamos implementarlo así:
// invoiceDictionary es de tipo Dictionary<string, Invoice>
var invoice = invoiceRepository.GetInvoice(42);
invoiceDictionary.Add(invoice.Year + "-" + invoice.SerialNumber, invoice);
Sin embargo, con tuplas queda bastante más claro y no tenemos que evaluar expresiones o consumir allocations a la hora de generar la clave:
// invoiceDictionary es de tipo Dictionary<(int year, int serialNumber), Invoice>
var invoice = GetInvoice(42);
invoiceDictionary.Add((invoice.Year, invoice.SerialNumber), invoice);
Internamente esto es posible porque las tuplas implementan automáticamente los métodos GetHashCode()
y Equals()
basándose en el valor de sus elementos.
6. Deconstrucción de objetos
Hace mucho tiempo ya hablamos en el blog de la deconstrucción de tuplas y clases en C#. Básicamente, la idea es que los objetos pueden ser descompuestos en un conjunto de valores que pueden ser recogidos posteriormente en forma de tuplas.
Para esto, podemos implementar el método Deconstruct()
en cualquier tipo de datos. Este método permite introducir un número indeterminado de parámetros de salida, que son los valores en los que se descompone el objeto. Como vemos en el siguiente ejemplo, la clase Person
es deconstruida en tres variables de salida (name
, age
e email
) que básicamente representan el estado completo de los objetos de este tipo:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
public void Deconstruct(out string name, out int age, out string email)
{
name = Name;
age = Age;
email = Email;
}
}
Más adelante, podemos obtener estos valores desde una tupla de forma muy sencilla, como se puede observar a continuación:
var person = _peopleRepository.GetPersonById(3422);
...
var (name, age, email) = person;
Console.WriteLine($"I'm {name}, {age} years old");
Console.WriteLine($"Contact me at {email}");
7. Definir matrices multidimensionales
Otra curiosidad de las tuplas es que pueden ser utilizadas para representar matrices multidimensionales. Aunque está bien saber que existe esta posibilidad, la verdad es que no creo que tenga demasiada utilidad práctica porque el resultado es aparentemente muy similar a los arrays, pero con menos posibilidades a la hora de gestionarlos:
var matrix = (
(1, 2, 3),
(4, 5, 6),
(7, 8, 9)
);
A diferencia de los arrays, los elementos no pueden ser accedidos mediante índices, sino que debemos usar los valores de las tuplas:
Console.WriteLine(matrix.Item2.Item3); // Item en fila 2, columna 3: 6
Otra diferencia es que, al no tratarse de una estructura fija, en realidad podríamos introducir estas matrices lo que queramos, porque los elementos de las tuplas son independientes unos de otros:
var matrix = (
( 1, 'a', 3),
(1.5, 5, "Six"),
( 7, 8, true)
);
Console.WriteLine(matrix.Item2.Item3); // Muestra: Six
8. Uso con pattern matching
switch
para mostrar distintos resultados en función de los valores de los elementos:var name = Console.ReadLine();
var grade = int.Parse(Console.ReadLine());
ShowGrade((name, grade));
void ShowGrade((string, int) student)
{
switch (student)
{
case ("Bill Gates", _):
Console.WriteLine("You are Bill Gates, so it doesn't matter");
break;
case (_, >= 5):
Console.WriteLine("You are a good student");
break;
case (_, < 5):
Console.WriteLine("You are not a good student");
break;
}
}
Publicado en Variable not found.
Picando Código
Paquete de eBooks: Ruby on Rails de Pragmatic Programmer
noviembre 13, 2023 07:52
Humble Bundle tiene un nuevo paquete de ebooks sobre Ruby y Ruby On Rails de Pragmatic Programmer. Pragmatic Programmer publica muy buenos libros de Ruby, y este es un excelente paquete. Los rumores de la muerte de Ruby y Rails han sido muy exagerados. No sólo está vivo y coleando, sino que es un lenguaje en constante evolución (y se paga muy bien).
Es una excelente oportunidad para adentrarse en el hermoso lenguaje de programación que es Ruby. Si bien este paquete incluye la cuarta versión del “Pickaxe” (Programming Ruby 1.9 & 2.0), es la última publicada hasta el momento y todavía es vigente. Es considerado el libro de referencia para Ruby (y de una versión anterior aprendí mucho Ruby personalmente). Lo básico todavía sirve, y podemos leer lo nuevo de Ruby 3 cuando salga la quinta edición en diciembre.
También me interesa Learn To Program con Ruby. Lo voy a mirar para ver si lo puedo recomendar a gente que quiera aprender a programar para que empiecen de una con Ruby!
Personalmente lo compré más que nada por Agile Web Development with Rails 7 (me tengo que poner al día con Rails después de no tocarlo en años), Ruby Performance Optimization suena interesante, Effective Testing with RSpec 3 seguro aprenda más sobre testing, Ruby Performance Optimization, y bueno, en general diría que todos me resultan interesantes menos el de Cucumber
Son 13 libros al precio que elijas, con un mínimo para obtenerlos todos que al momento de comprarlo eran £ 14.63. Los ebooks están disponibles en formatos PDF, MOBI y ePub, por lo que funcionan en casi cualquier dispositivo: computadoras, e-readers, iPads, teléfonos móviles y más. La compra también apoya a la caridad Active Minds una organización sin fines de lucro que trabaja en promover la salud mental en adultos jóvenes de 14 a 24 años y en cambiar la cultura entorno a cómo hablamos de salud mental.
Compra el paquete de ebooks Ruby y Rails en este enlace.
El post Paquete de eBooks: Ruby on Rails de Pragmatic Programmer fue publicado originalmente en Picando Código.Variable not found
Enlaces interesantes 544
noviembre 13, 2023 07:05

Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- El atributo CallerArgumentExpression, o cómo conocer las expresiones usadas como argumentos en llamadas a un método en C#
José María Aguilar - Funciones constructoras en Javascript
Oscar Sotorrío
.NET Core / .NET
- Reading a stream of JsonDocuments separated by new lines (ndjson)
Gérald Barré - Merging Multiple PDFs Using the iText Library
Matjaz Prtenjak - The convenience of System.IO
Richard Lander - Pangram validator in one line
Christian Heilmann - Implementing Distributed Locks in .NET Core with Redis or ZooKeeper
Hamed Salameh - MoonSharp - Running Lua Scripts in .NET
Khalid Abuhakmeh - Updates On Microsoft’s BinaryFormatter Obsoletion Strategy
DevExpress Blogs - Provide opt-in to experimental APIs using C#12 ExperimentalAttribute
Maarten Balliauw - Comparing ForEach Method and Foreach Statement in C#
Code Maze - Let’s build a custom Microsoft Graph connector
Waldek Mastykarz - Read and Write Windows Registry in C#
Code Maze - ConfigureAwait in .NET 8
Stephen Cleary - Discovering The Features Of DotNetFiddle – How To Compile C# Online
Nick Cosentino - .NET Task Parallel Library vs System.Threading.Channels
Charles Chen - TimeProvider makes it easier to mock time in .NET 8
David Grace - Automating Memory Profiling with the JetBrains dotMemory Profiler API
Steve Gordon - Interactive LINQ tutorial, especially for beginners
Steven Giesel
ASP.NET Core / ASP.NET / Blazor
- How to Add a UI or Dashboard as Middleware in your .NET Web API
Paul DeVito - ASP.NET Vertical Slice Project Template – How To Start Easily
Nick Cosentino - Where fetch data in your Blazor components
Jon Hilton - Reading JSON and binary data from multipart/form-data sections in ASP.NET Core
Andrew Lock - Hot Reload in ASP.NET Core Applications
Code Maze - Using Telerik UI for Blazor Forms and TileLayout
Jefferson S. Motta - Unveiling New Date-Only and Time-Only Features in Blazor Pickers
Prince Rajarathinam Oliver - ASP.NET Core Basics: Creating Apps with More Accessibility
Assis Zang - .NET 8–Http Logging
Bart Wullems
Azure / Cloud
- Setting up better logging in Azure Functions
Thomas Ardal
Conceptos / Patrones / Buenas prácticas
- Vertical Slice Architecture
Milan Jovanović - Why doesn't reduction by modulo work for floating point values?
Raymond Chen - Implications of Full-Duplex HTTP
IETF Datatracker - Event-Driven Architecture in C#
Code Maze
Data
- Moving SQL Server To The Cloud: Modernizing Stack Overflow for Teams
Aaron Bertrand - Leverage GitHub Copilot to work with MySQL in Azure Data Studio
Sunitha Muthukrishna - Single() or First()? Understand the Abstractions you use!
Derek Comartin
Machine learning / IA / Bots
- OpenAI DevDay 2023: todas las novedades en el mundo del líder en IA generativa
CampusMVP - Construye un Asistente Personal con super poderes de IA Generativa
Elizabeth Fuentes & Ensamblador - New models and developer products announced at DevDay
OpenAI - Introducing GPTs
OpenAI - Unleashing the Power of C#: Integrating AI for Intelligent Applications
Darren Horricks - Assistants: the future of Semantic Kernel & OpenAI Assistants: a first look into using OpenAI Assistants with Semantic Kernel
Matthew Bolanos
Web / HTML / CSS / Javascript
- Introducing Angular v17
Minko Gechev - Writing Components That Work In Any Frontend Framework
Andrico Karoulla - Virtual Scrolling In React
Tech with Vedansh - Using a strong nonce based CSP with Angular
Damien Bowden - A new way to bring garbage collected programming languages efficiently to WebAssembly
Alon Zakai - Svelte Flow – a library for rendering interactive node-based UIs
John Robb - Understanding React Error Boundary
Dianne Pena - JavaScript Map and Set Tutorial
Rob Gravelle - 11 JavaScript Tricks You Probably Didn't Know About
Fadhili Josue - How Core Web Vitals saved users 10,000 years of waiting for web pages to load
Chromium Blog - Addressing Accessibility Concerns With Using Fluid Type
Maxwell Barvian - Angular.dev and v17—I Told You the Renaissance was Here
Alyssa Nicoll - Announcing Angular.dev
Emma Twersky - Chrome Console Utilities That Every Developer Should Know
Shalitha Suranga - React Router v6: A Beginner's Guide
James Hibbard - TypeScript Generics: Striking the Right Balance
Suprotim Agarwal - Understanding source maps and their types in Javascript
Anton Ioffe - Creating Accessible UI Animations
Oriana García - The World of Sparse Arrays in JavaScript
Corina Murg - Secure your Web Applications with Facial Authentication
Pius oruko - 67 Weird Debugging Tricks Your Browser Doesn't Want You to Know
Alan Norbauer
Visual Studio / Complementos / Herramientas
- Configure Visual Studio to Name Private Fields with Underscore
Steve Smith - Visual Studio vs Visual Studio Code: The Ultimate Guide
NDepend - How JetBrains Rider Implemented .NET WebAssembly Debugging
Sasha Ivanova - That's Why I Love Interactive Rebase
Lucas Rocha - ReSharper 2023.3 EAP 6: Code Analysis Performance Improvements, Support for C# 12 Interceptors, and Much More.
Rachel Appel
.NET MAUI / Xamarin
- Easily Bind SQLite Data to .NET MAUI ListView and Perform CRUD Actions
Jayaleshwari N. - From Text to Action: Creating Hyperlinks in .NET MAUI
Leomaris Reyes
Otros
Publicado en Variable not found.
Blog Bitix
Desempaquetado de lector de libros electrónico Amazon Kindle Paperwhite (11ª generación)
noviembre 09, 2023 08:00
Una parte del tiempo libre que dispongo la dedico a la lectura, de novelas y libros técnicos principalmente. Tras tener un Kindle Touch del 2012 y venderlo me he vuelto a comprar un Kindle Paperwhite de 11ª generación. Tras casi 10 años de diferencia entre ambos dispositivos las mejoras técnicas entre ambos dispositivos son notables, teniendo el Paperwhite una pantalla mucho mejor e iluminación entre otros añadidos que hacen del Paperwhite un salto grande y merezca el cambio de dispositivo.
Blog Bitix
Desempaquetado de lector de libros electrónico Amazon Kindle Paperwhite
noviembre 09, 2023 08:00
Una parte del tiempo libre que dispongo la dedico a la lectura, de novelas y libros técnicos principalmente. Tras tener un Kindle Touch del 2012 y venderlo me he vuelto a comprar un Kindle Paperwhite de 11ª generación. Tras casi 10 años de diferencia entre ambos dispositivos las mejoras técnicas entre ambos dispositivos son notables, teniendo el Paperwhite una pantalla mucho mejor e iluminación entre otros añadidos que hacen del Paperwhite un salto grande y merezca el cambio de dispositivo.
Picando Código
Error: EDID block 0 (tag 0x00) checksum is invalid, remainder is N
noviembre 09, 2023 06:12
Hace poco compré una Raspberry Pi 4. Estoy muy contento con ella, a pesar de que al poco tiempo saliera la versión 5 con hardware mucho mejor
La tengo en su case muy prolija, con el sistema instalado en un disco duro SSD, conectada a mi televisor. Uno de sus usos es para mirar servicios de streaming, películas y series.
Hace poco me empezó a pasar que dejaba de transmitir la imagen al televisor. Recomiendo de primera habilitar la conexión SSH en Raspbian (o el sistema que tengan instalado), para poder investigar cualquier problema desde otra computadora como es el caso de este post.
Como comentaba, si bien la Raspberry estaba prendida, no había imagen. Así que me conecté por SSH y empecé a buscar qué podía estar pasando. El servidor gráfico y el escritorio estaban todos funcionando aparentemente bien. Ejecuté el comando dmesg
para ver los mensajes del kernel. Podemos mandarlo por pipe a less
para hacer más práctica la lectura e investigación:
Hay mucha información, pero esto me llamó la atención. Siempre es un buen indicativo de error ver “BAD” en el log:
[ 3.172944] [00] BAD 00 ff ff ff ff ff ff 00 4c 2d 7a 0a 00 00 00 00
[ 3.172957] [00] BAD 2e 16 01 03 80 59 32 78 0a ee 91 a3 54 4c 99 26
[ 3.172968] [00] BAD 0c 50 54 bd ef 80 71 4f 81 c0 81 00 81 80 95 00
[ 3.172979] [00] BAD a9 c0 b3 00 01 01 02 3a 80 18 71 38 2d 40 58 2c
[ 3.172990] [00] BAD 45 00 24 72 42 00 00 1e 66 21 56 aa 51 00 1e 30
[ 3.173000] [00] BAD 46 8f 33 00 24 72 42 00 00 1e 00 00 00 fd 00 18
[ 3.173011] [00] BAD 4b 0f 51 17 00 0a 20 20 20 20 20 20 00 00 00 fc
[ 3.173021] [00] BAD 00 53 41 4d 53 55 4e 47 0a 20 20 20 20 20 01 6b
[ 3.223212] EDID block 0 (tag 0x00) checksum is invalid, remainder is 110
Buscando en internet me encontré con este mensaje en los foros de OpenSuse.
EDID se refiere a “Extended display identification data” (en wikipedia). Se trata de la información que manda el monitor a la computadora sobre sí mismo. Y en teoría el error significa que la comunicación entre el monitor y la computadora no está funcionando bien, el paquete de información EDID no se recibió correctamente. Según dice el foro el problema se debe generalmente a cables de baja calidad. No sé si este es exactamente el problema que hacía que no hubiera imagen (ahí hablan más bien de que hay imagen pero con una resolución menor a la soportada).
Pero al conectar y desconectar el cable HDMI funcionó de nuevo. Volvió a pasar otra vez y terminé cambiando el puerto HDMI en el que conectaba la Raspberry Pi al televisor. Tanto el cable como el puerto podrían ser el problema. El cable debe tener sus años ya. Pero si tuviera que adivinar, me la jugaría a que es el televisor. Conseguí mi televisor de segunda mano, un amigo me lo vendió a un precio simbólico al comprarse un televisor nuevo. Ya tenía el uso que le dió mi amigo, ahora tiene 6 años de uso más. Hay partes de la pantalla que están como más oscuras que el resto… No te digo, te muestro:
Esa sección a la izquierda donde se ve como más oscuro a la mitad de la pantalla, se nota bastante. Sí, sería tiempo de cambiar de tele… Sólo de pensar lo difícil que va a ser conseguir una tele boba que no tenga que conectar a internet.
Pero bueno, mientras no compro un televisor nuevo, sigo usando éste con mi Raspberry Pi. He hecho alguna cosa más con esta divertida computadora, ya comentaré más al respecto
Foto del raspberry usado en la imagen destacada por Mockup Graphics en Unsplash.
El post Error: EDID block 0 (tag 0x00) checksum is invalid, remainder is N fue publicado originalmente en Picando Código.Picando Código
La Ley de Stallman hizo que perdiera interés en gran parte de las nuevas tecnologías
noviembre 07, 2023 05:41
Desde que era joven y leía la sección de tecnología en las revistas “Newsweek” que llegaban al trabajo de mi padre -entre otras tantas revistas-, me apasionaba por todo lo relacionado a nueva tecnología. Alguna novedad de robótica, Internet, computadoras, electrodomésticos, cada tanto hasta algo sobre videojuegos, incluso juguetes electrónicos novedosos.
Hoy tenemos cosas nuevas en tecnología prácticamente a diario: Nuevos servicios web, nueva red social por algún cocainómano millonario, nuevos dispositivos móviles, electrodomésticos con Bluetooth y Wi-Fi por alguna razón, automóviles con software que necesita actualizarse, televisores inspirados en los de 1984, despidos masivos y medidas anti-sindicatos, avances en “Inteligencia Artificial” (cuando se pase esta moda que reemplazó las criptomonedas, ¿le cambiarán el nombre a “pseudo IA” o “proto IA”?)… y en la mayoría de los casos no podría interesarme menos.
Escribí en este blog sobre la Ley de Stallman en 2012. Creo que desde entonces la cambiaron o mi traducción no era 100% acertada. La Ley de Stallman (hoy) dice:
Now that corporations dominate society and write the laws, each advance or change in technology is an opening for them to further restrict or mistreat its users.
Traducción: Ahora que las corporaciones dominan a la sociedad y escriben las leyes, cada avance o cambio tecnológico les brinda una nueva oportunidad para restringir o maltratar aún más a los usuarios.
Sin recordar esta ley explícitamente, he notado ese cambio en mi apreciación de la tecnología, lo he leído de otras personas en Internet y lo he conversado con varios amigos. Recientemente lo hablaba con Máximo y Grilix. Cada cambio o novedad, nos preguntamos “¿Cómo nos van a cagar con esto?”. Los capitalistas arruinaron ese sentimiento de asombro y curiosidad, por reflejo nos ponemos a la defensiva y nos preparamos para una nueva amenaza tecnológica. Si algo es bueno, será cuestión de tiempo para que se mierdifique. Un panorama desolador…
Vivimos en una distopía, así como las que tanto hemos leído en ciencia ficción, pero la más aburrida posible. A nuestros supervillanos les falta la parte que los hace interesantes en las historias de ficción, eso de que cada tanto se les caía una buena idea también.
¡Pero queda esperanza!
Por suerte todavía hay lugares donde encuentro interés en la innovación digital y demás novedades. Uno de ellos sigue siendo el software libre y particularmente en tiempos recientes el Fediverso. El hecho de que no haya un CEO millonario a cargo como la gran mayoría de los otros casos, hace que la innovación no sea manejada por “cómo podemos sacar la mayor ganancia posible de esto”.
Así que todavía sigo leyendo sobre novedades tanto del software libre en general como del fediverso. Toda tecnología que sea más comunitaria y solidaria, por lo menos le presto un poco de atención. Y ahí es donde realmente se ve innovación interesante para mí, por el lugar de donde surge.
También viene resurgiendo un movimiento “ludita”, [Everyone is a luddite now, The Luddites warned us about Google] gente que no está necesariamente en contra de la tecnología, pero sí de su uso en como medio de explotación. Tengo en mi lista para leer el libro que comentan en estos dos artículos: “Blood in the Machine: The Origins of the Rebellion Against Big Tech” por Brian Merchant. Ya lo comentaré cuando lo lea y aprenda un poco más del tema.
El otro lugar donde todavía encuentro interés es en los videojuegos. Teniendo en cuenta que las empresas líderes no dejan de ser corporaciones millonarias en búsqueda de ganancia, creo que todavía existe mucha gente ahí tratando de innovar y hacer cosas divertidas. Y al final del día son una forma de entretenimiento y escape que ayuda, sobretodo en este clima tan opresivo en el que me siento a veces.
Me sigue resultando interesante seguir innovación tanto en hardware como en software, así como continuar viendo cómo evoluciona el gaming con el que crecí. Está muy bueno cómo se siguen creando cosas nuevas en base a tecnología “antigua”, como el mod para controles de Nintendo 64 de 8BitDo.
Sigue habiendo más lugares donde encuentro interesantes avances en la tecnología. Pero en lo más general, no sólo no lo sigo, sino que intento proactivamente evitarlo para no llenar mi cerebro con información negativa que no me interesa.
Hasta acá mi “gritarle a una nube” de hoy. Espero que lo hayas disfrutado, te invito a gritarle a una nube en los comentarios.
El post La Ley de Stallman hizo que perdiera interés en gran parte de las nuevas tecnologías fue publicado originalmente en Picando Código.Variable not found
Formatear cadenas interpoladas en C#
noviembre 07, 2023 07:05

El otro día me descubrí escribiendo un código parecido al siguiente:
return $"Order: {Items.Length} items, {Total.ToString("#,##0.#0")}";
Mal, lo que se dice mal, no estaba; funcionaba perfectamente y cumplía los requisitos, pero me di cuenta de que no estaba aprovechando todo el potencial de las cadenas interpoladas de C# que ya habíamos comentado por aquí mucho tiempo atrás.
Y como siempre que tengo algún despiste de este tipo, pienso que quizás pueda haber alguien más al que le ocurra o no esté al tanto de esta posibilidad el lenguaje, así que vamos a ver cómo podíamos haberlo implementado de forma algo más simple.
Normalmente, cuando usamos interpolación, todo lo que está entre llaves lo entendemos como una expresión C#, cuya conversión a string
es lo que incluiremos finalmente en la cadena resultante.
Sin embargo, es bueno recordar que la interpolación permite una sintaxis más concisa para cubrir la (habitual) necesidad de tener que formatear el texto antes de incluirlo en la cadena.
El carácter ":"
permite separar la expresión a evaluar del formato a aplicarle. Por tanto, la línea anterior podría haber quedado simplemente como:
return $"Order: {Items.Length} items, {Total:#,##0.#0}";
¡Mucho mejor así! El resultado en tiempo de ejecución será el mismo, pero el código es más corto y legible.
Por supuesto, podemos usar esta sintaxis con cualquier tipo de dato, y todos los formatos que usamos habitualmente en las sobrecargas de ToString()
o llamadas a string.Format()
:
Console.Write($"Hoy es: {DateTime.Now:dd-MM-yyyy}");
Publicado en: www.variablenotfound.com. Picando Código
Paquete oficial .deb de Mozilla Firefox Nightly para Debian y Ubuntu
octubre 30, 2023 03:00
Mozilla anunció la publicación de un repositorio APT para instalar Firefox Nightly como paquete .deb. Esto facilita la instalación en distribuciones Linux basadas en Debian como Ubuntu, Linux Mint y demás. Los paquetes son compatibles con las mismas versiones de Debian y Ubuntu que los archivos binarios tradicionales que distribuyen. Si ya tenemos funcionando una de esas versiones, podemos cambiarnos al repositorio APT oficial de Mozilla para instalar y actualizar la aplicación junto con el resto del sistema en el gestor de paquetes.
Uso Firefox Nightly desde hace años, y siempre descargaba los binarios en mi directorio home en ~/bin
. Después tenía que agregar un archivo .desktop a mano para agregar la aplicación a los lanzadores de aplicaciones o hacer un acceso directo. Instalando desde el repositorio, no lo tengo que hacer más. Trae también otros beneficios:
- Mejor rendimiento gracias a las optimizaciones avanzadas basadas en el compilador.
- Binarios más seguros con todas las banderas de seguridad habilitadas durante la compilación
- Las actualizaciones más al día lo antes posible porque el paquete está incorporado al proceso de publicación de Firefox.
Para configurar el repositorio APT en nuestros sistemas, tenemos que ejecutar lo siguiente:
wget -q https://packages.mozilla.org/apt/repo-signing-key.gpg -O- | gpg –dearmor | sudo tee /etc/apt/trusted.gpg.d/packages.mozilla.org.gpg > /dev/null
# La fingerprint debería ser 35BA A0B3 3E9E B396 F59C A838 C0BA 5CE6 DC63 15A3
gpg –quiet –no-default-keyring –keyring /etc/apt/trusted.gpg.d/packages.mozilla.org.gpg –fingerprint | awk ‘/pub/{getline; gsub(/^ +| +$/,””); print “\n”$0″\n”}’
# Luego agregamos el repositorio a nuestra lista de fuentes
echo “deb [signed-by=/etc/apt/trusted.gpg.d/packages.mozilla.org.gpg] https://packages.mozilla.org/apt mozilla main” | sudo tee -a /etc/apt/sources.list.d/mozilla.list > /dev/null
# Actualizamos la lista de paquetes e instalamos el paquete .deb de Firefox Nightly:
sudo apt-get update && sudo apt-get install firefox-nightly
Y tenemos Firefox Nightly instalado.
Para los paquetes de idioma, tenemos que instalar el paquete específico. Los podemos buscar con apt-cache search firefox-nightly-l10n
:
firefox-nightly-l10n-es-ar – Mozilla Firefox Nightly – Firefox Nightly Language Pack for Español (AR) (es-AR) – Spanish, Argentina
firefox-nightly-l10n-es-cl – Mozilla Firefox Nightly – Firefox Nightly Language Pack for Español (CL) (es-CL) – Spanish, Chile
firefox-nightly-l10n-es-es – Mozilla Firefox Nightly – Firefox Nightly Language Pack for Español (ES) (es-ES) – Spanish, Spain
firefox-nightly-l10n-es-mx – Mozilla Firefox Nightly – Firefox Nightly Language Pack for Español (MX) (es-MX) – Spanish, Mexico
Metodologías ágiles. De lo racional a la inspiración.
Scrum master a tiempo completo: 42 Tareas
octubre 26, 2023 09:11
Blog Bitix
Novedades y nuevas características de Java 21
octubre 19, 2023 04:00
Java 21 como versión LTS es una versión especial. Los virtual threads junto con las sequenced collections y las nuevas posibilidades de pattern matching quizá son las novedades más destacadas de esta versión. Otras novedades interesantes en el futuro siguen en modo vista previa.
Header Files
Cómo llamar a una función una única vez
octubre 13, 2023 08:00
Introducción
Algunas veces es necesario tener funciones que han de llamarse una única vez en todo el ciclo de vida del proceso. El caso que más he visto es el de funciones de inicialización, tales como la configuración de un framework de terceros, la definición de variables de entorno o la creación de zonas de memoria compartidas.
Como pasa muchas veces, C++ nos ofrece no una, sino muchas formas de resolver el problema: estudiemos algunas de ellas (spoiler, dejaré mi favorita para el final). Para facilitar las explicaciones, asumiremos que el código a ejecutar está encapsulado en una función llamada init_once()
que debe ser llamada antes de que execute_many()
se ejecute.
Variable bandera
Seguramente la solución más sencilla, aunque no necesariamente la más eficiente, es crear una variable a modo de bandera de uso (inicializada a false), y cambiarla la primera vez que se llame a la función.
namespace
{
bool g_called{false};
}
void execute_many()
{
if (!g_called) {
init_once();
g_called = true;
}
// ...
}
Variante con variable estática
Personalmente prefiero limitar el alcance de las variables todo lo posible, por lo que cambiaremos esta bandera a una variable estática local. Recordad que una variable estática se crea una única vez y perdura durante toda la vida del proceso.
void execute_many()
{
static bool s_called{false};
if (s_called) {
init_once();
s_called = true;
}
// ...
}
Si bien presentan una solución simple, queda la sutil posibilidad de que cambiemos el valor de la bandera por error (por ejemplo, si tenemos varias funciones de inicialización). El tema de la eficiencia claramente dependerá del contexto, aunque la gran mayoría de las veces no será un problema. Por último, estas soluciones podrían originar una condición de carrera y desembocar en una doble inicialización.
std::call_once
C++11 introdujo una forma estándar de resolver este problema, y que además es thread-safe. Como ya se dijo, las dos soluciones anteriores pecarían de crear condiciones de carrera, necesitando el uso de mutex adicionales; el uso de std::call_once
es equivalente pero mucho más limpio. Básicamente sigue el mismo modelo que la solución anterior: se asocia un flag especial (thread-safe) a la función que queremos llamar una única vez:
#include <mutex>
void execute_many()
{
static std::once_flag s_once;
std::call_once(s_once, init_once);
// ...
}
Uso de singletons
Otra posible solución es emplear un singleton. Un singleton es un patrón de diseño que permite restringir la creación de objetos de una clase a una única instancia. Así, podemos utilizarlo para llamar a init_once()
durante la construcción del mismo (y como la clase sólo se construye una vez, sólo se llamará a la función una única vez). Una ventaja de este método frente a los anteriores es que nos evitamos la comprobación de una bandera de estado para cada ejecución. Si la función execute_many()
se llama de forma masiva, pues es una mejora que ganamos. En contrapartida, la función execute_many
pasa a ser miembro del singleton.
Acá una implementación sencilla pero suficiente de un singleton con inicialización única:
class Singleton
{
public:
Singleton& get_instance() {
static Singleton s_singleton;
return s_singleton;
}
void execute_many() { /* ... */ }
private:
Singleton() {
init_once();
}
};
void foo()
{
Singleton::get_instance().execute_many();
}
Usando el operador de evaluación secuencial en la inicialización de una variable estática
La última solución que expondré es, para mí, la más limpia en términos de código generado, aunque requiere un poco más de conocimiento del lenguaje para poder entenderla. Expliquemos primero las partes que lo componen:
Operador de evaluación secuencial
El operador de evaluación secuencial es una expresión del tipo (e0, e1, …, en), donde las sub-expresiones ei son evaluadas en orden y cuyo tipo y valor final corresponden a los de en. Así, la siguiente expresión auto x = (42.0f, "hola"s)
resultaría en x
de tipo std::string
y con valor "hola"
. Si una de las sub-expresiones fuese una llamada a función, ésta se invocaría, independientemente del tipo de retorno de la misma, incluido void
. Por otra parte, si una de las sub-expresiones lanza una excepción, las siguientes sub-expresiones no serían evaluadas.
int a = 0;
std::cout << (a++, ++a, a) << std::endl;
try {
(a++, throw std::exception{}, a--); // a-- is never called
} catch(...) {
std::cout << "Exception" << std::endl;
}
std::cout << a << std::endl;
El resultado es:
2
Exception
3
Nótese que como son expresiones separadas, evaluadas secuencialmente, el uso del operador de post-incremento no se diferencia (en cuanto al resultado final) del de pre-incremento.
Inicialización de variable estáticas
Por otro lado, las variables estáticas sólo se construyen una vez, y el estándar de C++ garantiza que la inicialización de una variable estática es thread-safe; es decir, si diversos hilos pasan concurrentemente por la inicialización de la variable, sólo uno de ellos, el primero, la efectuará, quedando los demás bloqueados hasta que finalice la inicialización.
Ensamblando las partes
Con todo esto podemos construir una versión minimalista de nuestra solución, que garantizará que la función init_once()
será llamada una única vez, de forma thread-safe y sin comprobaciones innecesarias de banderas de estado.
void execute_many()
{
static const bool s_initialized = (init_once(), true);
// ...
}
Extendiendo la solución
El principio de responsabilidad única conlleva, por lo general, a descomponer nuestro código en clases y funciones con una finalidad más acotada. En el caso que nos ocupa hoy esto puede suponer aumentar el riesgo de que la función init_once()
sea llamada desde diversos lugares, debiendo aplicar los mecanismos de protección expuestos más de una vez. Esto nos lleva al eterno dilema del programador: evitar duplicar código innecesariamente.
En términos generales, la solución pasa primero por limitar el acceso a la función en sí misma. Una primera forma de hacerlo es crear una clase cuya única razón de ser sea la de invocar a esta función:
class InitOnceCaller
{
public:
static void call_init_once()
{
static const bool s_initialized = (init_once(), true);
}
private:
static void init_once() { /* ... */ }
};
La contrapartida acá es que debemos pagar por una llamada a función adicional en caso de que el compilador no la haga inline.
En caso de que la función deba ser llamada únicamente desde un punto en concreto, podríamos mover init_once()
a una lambda local.
void execute_many()
{
static const auto s_init_once = []() { /* ... */ };
static const bool s_initialized = (s_init_once(), true);
}
Conclusión
Se han presentado varias formas de abordar el problema de inicialización única, yendo desde la más obvia y sencilla, hasta la más completa (aunque sutilmente críptica para los menos entendidos en el lenguaje), pasando por opciones intermedias en cuanto a legibilidad y rendimiento.
Juanjo Navarro
Mini Curso: Pair Programming with a Large Language Model
octubre 02, 2023 09:32
DeepLearning.AI sigue sacando cada poco tiempo sus cursos cortos.
Se trata de pequeños cursos de una duración de alrededor de 1 hora. Aunque son muy cortos, facilitan mucho el aprendizaje inicial de una tecnología ya que proporcionan junto al video un cuaderno python donde podemos practicar, con todos los requisitos preinstalados y con un token de acceso al API de turno. Digamos que es la forma más rápida de tener un entorno montado para practicar.
Puesto que son tan cortos, es difícil no sacar algo en positivo. No obstante creo que este nuevo curso que acaban de publicar, Pair Programming with a Large Language Model, es quizá de los peor planteados.
Como parte positiva, se puede practicar con el API de Bard, lo cual si vives en Europa es especialmente práctico (en estos momentos dicho API no está accesible desde Europa). Aprendes a importar el API y a realizar las llamadas básicas para interrogar al LLM.
En la parte negativa la verdad es que está el resto. El curso no tiene mucho sentido ya que su objetivo es utilizar el API de Bard para realizar “consultas de programación” y para utilizar el LLM de Google como un “compañero de programación”: Ayudarte a programar, a debuggear, etc. Francamente, para eso no hace falta utilizar el API y se puede utilizar el chat de Bard directamente. Además, como “compañero” Bard resulta ser bastante flojo, con respuestas poco acertadas o directamente erróneas, por lo que te quedas con la sensación de que el LLM de Google está por detrás de la competencia.
Flojito.
Juanjo Navarro
Pequeño selector de sesiones para .ssh/config
septiembre 22, 2023 06:29
Un pequeño selector de sesiones para ssh, por si a alguien le pudiese ser útil. Sirve para seleccionar fácilmente la máquina a la que queremos conectarnos de entre las que tenemos configuradas en .ssh/config
.
Es una sencilla función para bash, simplemente añadirla a .bashrc
:
hosts() {
local host_list=`grep "Host .*$1.*" $HOME/.ssh/config | awk '{print $2}' | sort `
clear
local result=$(DIALOGRC="$HOME/.dialogrc_hosts" dialog --clear --output-fd 1 --no-items --menu "Host" 0 0 0 $host_list)
clear
if [[ ! -z $result ]]
then
ssh $result
fi
}
Una vez instalada la función, podemos ejecutar hosts
para obtener un diálogo donde elegir el host al que nos queremos conectar:

Podemos filtrar el listado de elementos que se nos muestra, por ejemplo ejecutando hosts bbdd
:

Finalmente, si preferimos que el diálogo se muestre sin colores, crear este fichero .dialogrc_hosts
en el directorio del usuario:
# Set aspect-ration.
aspect = 0
# Set separator (for multiple widgets output).
separate_widget = ""
# Set tab-length (for textbox tab-conversion).
tab_len = 0
# Make tab-traversal for checklist, etc., include the list.
visit_items = OFF
# Shadow dialog boxes? This also turns on color.
use_shadow = OFF
# Turn color support ON or OFF
use_colors = ON
# Screen color
screen_color = (BLACK,BLACK,OFF)
# Shadow color
shadow_color = (BLACK,BLACK,OFF)
# Dialog box color
dialog_color = (BLACK,WHITE,OFF)
# Dialog box title color
title_color = (BLACK,WHITE,OFF)
# Dialog box border color
border_color = (BLACK,WHITE,OFF)
# Active button color
button_active_color = (WHITE,BLACK,ON)
# Inactive button color
button_inactive_color = dialog_color
# Active button key color
button_key_active_color = button_active_color
# Inactive button key color
button_key_inactive_color = (BLACK,WHITE,OFF)
# Active button label color
button_label_active_color = (WHITE,BLACK,OFF)
# Inactive button label color
button_label_inactive_color = (BLACK,WHITE,OFF)
# Input box color
inputbox_color = dialog_color
# Input box border color
inputbox_border_color = dialog_color
# Search box color
searchbox_color = dialog_color
# Search box title color
searchbox_title_color = title_color
# Search box border color
searchbox_border_color = border_color
# File position indicator color
position_indicator_color = title_color
# Menu box color
menubox_color = dialog_color
# Menu box border color
menubox_border_color = border_color
# Item color
item_color = dialog_color
# Selected item color
item_selected_color = button_active_color
# Tag color
tag_color = title_color
# Selected tag color
tag_selected_color = button_label_active_color
# Tag key color
tag_key_color = button_key_inactive_color
# Selected tag key color
tag_key_selected_color = (WHITE,BLACK,ON)
# Check box color
check_color = dialog_color
# Selected check box color
check_selected_color = button_active_color
# Up arrow color
uarrow_color = (GREEN,WHITE,ON)
# Down arrow color
darrow_color = uarrow_color
# Item help-text color
itemhelp_color = (WHITE,BLACK,ON)
# Active form text color
form_active_text_color = button_active_color
# Form text color
form_text_color = (WHITE,BLACK,ON)
# Readonly form item color
form_item_readonly_color = (BLACK,WHITE,ON)
# Dialog box gauge color
gauge_color = title_color
# Dialog box border2 color
border2_color = dialog_color
# Input box border2 color
inputbox_border2_color = dialog_color
# Search box border2 color
searchbox_border2_color = dialog_color
# Menu box border2 color
menubox_border2_color = dialog_color
Utiliza la utilidad dialog
por lo que es necesario instalarla desde nuestro gestor de paquetes si no está ya instalada.
Navegapolis
Origen de scrum más allá de los mitos
septiembre 04, 2023 10:28
Es fácil determinar quién creó el modelo de procesos CMMI o el de escalado de agilidad SAFe —por ejemplo—. La Universidad Carnegie Mellon desarrolló CMMI y Dean Leffingwell es el creador de SAFe. Son sus autores y por lo tanto los propietarios de todos los derechos. Ellos definen cómo es cada modelo y sólo ellos los pueden modificar o decidir si otros pueden hacerlo.
¿Se puede decir lo mismo de scrum? ¿Es un modelo con autoría y propiedad o una corriente emergente, colaborativa y compartida?
Hay quien afirma que los autores de scrum fueron Hirotaka Takeuchi y Ikujiro Nonaka en 1986, al identificar en el artículo “The New New Product Development Game”(1) los principios del modo de trabajo incipiente, al que expontáneamente estaban dando forma los equipos de empresas como 3M, Fuji, Honda o HP .
En los años 80, los ingenieros de muchas empresas, descontentos de los modelos de producción basados en ciclos de cascada, desarrollaban —en ambientes de trabajo flexibles y colaborativos— un patrón iterativo con solapamiento de fases, que Nonaka y Takeuchi identificaron y bautizaron con el término “scrum”, palabra empleada en rugby para definir la formación de los jugadores al disputar la pelota.
Es importante entender que Nonaka y Takeuchi identificaron, dieron nombre y visibilidad al concepto, pero no lo crearon.
Scrum, no es una metodología diseñada y difundida verticalmente, desde arriba hacia abajo; desde la autoría de una universidad, un comité de estandarización o un investigador reconocido. Scrum emergió desde la abajo, impulsado y construido horizontalmente por la comunidad profesional. Es un ejemplo brillante de conocimiento abierto que a diferencia de los modelos de conocimiento acotado o cerrado no tiene un propietario que lo define. Es de dominio público.
También se afirma que los creadores de scrum fueron Peter DeGrace y Leslie Hulet Stahl en 1990 al presentar en su libro “Wicked Problems, Righteous Solutions”(2) scrum como una propuesta de desarrollo iterativo, basado en los principios de Nonaka y Takeuchi.
Muchos, sin embargo, afirman que los autores de scrum son Jeff Sutherland y Ken Schwaber, porque en 1995 presentaron su propia interpretación de scrum en el congreso OOPSLA(3); otro marco de desarrollo iterativo basado en los principios identificados por Nonaka y Takeuchi, con un ciclo formado por tres estadíos: “prejuego, juego de iteraciones y postjuego”
Pero lo cierto es que hoy el estándar generalizado de scrum, es muy diferente a las primeras propuestas de los años 90, porque desde su origen como antítesis al desarrollo en cascada, surgida en empresas innovadoras de los años 80, scrum es un conocimiento profesional, emergente y abierto que se viene enriqueciendo de forma continua con la convergencia de ideas y prácticas aportadas desde la comunidad ágil.
La reducción de las iteraciones, desde las duraciones iniciales de 1 o 2 meses, a las actuales de una o dos semanas, la incorporación de las reuniones retrospectivas, el refinado de la pila de producto, las diferentes prácticas y aportaciones para estimar las tareas e historias de usuario o para no estimarlas en absoluto, las propuestas de tableros kanban de gestión visual y un largo etcétera, son ejemplos de cómo de forma continua, la comunidad ágil viene madurando y enriqueciendo scrum.
Pensar en un autor y en una “guía oficial” es un error. Es considerar cerrado y propietario, a un conocimiento colaborativo y abierto. Al hacerlo lo encorsetamos y le cortamos las alas, ignorando que es de dominio público, fruto de la inteligencia colectiva aportada desde la comunidad ágil.
Actualmente se ofrece formación y difusión de scrum desde las dos perspectivas: la de conocimiento propietario y la de conocimiento abierto. Posiblemente, las más conocidas entre las que enseñan scrum como un modelo propietario y cerrado, sean las dos que se anuncian como autoras del mismo: Scrum.org y Scruminc. Y posiblemente la más conocida que enseña scrum como un modelo de conocimiento abierto sea Scrum Manager, con la que obviamente me identifico.
A medida que avanzamos en el campo de la agilidad, es esencial entender que cada ruta de aprendizaje ofrece su propio conjunto de ventajas y perspectivas. Que la diversidad de enfoques da riqueza y versatilidad a scrum como marco de trabajo.
Para quienes prefieren un camino estructurado con una definición acotada de scrum, organizaciones como scrum.org o scruminc (y otras muchas) proporcionan un marco con una definición acotada que puede funcionar como un sólido punto de partida. Esta perspectiva puede ser especialmente útil para aquellos que empiezan en scrum o que buscan una base sólida y comúnmente reconocida, bien para usarla directamente o bien como punto de partida de su propio “shu ha ri”.
Por otro lado, para aquellos que ven scrum como conocimiento “commons“, emergente y evolutivo, plataformas como Scrum Manager ofrecen un conocimiento abierto.
Reconocen que scrum es un punto de partida y que su verdadera potencia se desarrolla cuando los equipos comprenden la agilidad y aprenden a desprenderse del marco técnico estándar y a modelar el propio, con las prácticas más adecuadas a sus necesidades específicas y contextos únicos.
En última instancia, la elección de una u otra perspectiva, dependerá de las necesidades, el contexto y la filosofía del individuo o equipo en cuestión. Lo más importante es que, independientemente del camino elegido, continuemos respetando el espíritu de mejora continua y colaboración que está en el corazón de scrum.
(1) Hirotaka Takeuchi & Ikujiro Nonaka, 1986 “The New New Product Development Game”, Harvard Business Review.
(2) Peter DeGrace & Leslie Hulet Stahl, 1990 “Wicked Problems, Righteous Solutions”(págs. 153-157), 1990, Prentice Hall.
(3) Scrum Development Process, 1995, Ken Schwaber OOPSLA Business Object Design and Implementation Workshop.
La entrada Origen de scrum más allá de los mitos se publicó primero en Navegápolis.
Una sinfonía en C#
Cómo enviar datos desde .NET a Prometheus
septiembre 04, 2023 12:00
Introducción
Prometheus es un sistema de monitoreo de código abierto que nos permite almacenar series de tiempo de datos numéricos. Es muy utilizado para monitorear aplicaciones y servicios en producción. En este caso vamos a ver cómo enviar datos desde una aplicación .NET a Prometheus.
Agregar el paquete NuGet
Utilizaremos https://github.com/prometheus-net/prometheus-net en nuestra aplicación .NET.
Y agregaremos el siguiete código
internal class Program
{
static void Main(string[] args)
{
using var server = new KestrelMetricServer(port: 1234);
server.Start();
// Generate some sample data from fake business logic.
var recordsProcessed = Metrics.CreateCounter("sample_records_processed_total", "Total number of records processed.");
_ = Task.Run(async delegate
{
while (true)
{
// Pretend to process a record approximately every second, just for changing sample data.
recordsProcessed.Inc();
await Task.Delay(TimeSpan.FromSeconds(1));
}
});
Console.WriteLine("Listening on port 1234");
Console.ReadLine();
}
}
Con este tenemos por un lado las métricas por defecto que nos da Prometheus y por otro lado una métrica que nosotros creamos llamada sample_records_processed_total
que es un contador que incrementa cada segundo.
Configurar Prometheus para que lea nuestros datos
global:
scrape_interval: 5s
evaluation_interval: 5s
scrape_configs:
- job_name: 'my_net_application'
static_configs:
- targets: ['host.docker.internal:1234']
Llamaremos a este archivo prometheus.yml
y se copiará en la carpeta donde Prometheus busca su configuración.
Ejecutar Prometheus desde Docker
version: "3.7"
services:
prometheus:
image: prom/prometheus
container_name: prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
ports:
- 9090:9090
restart: always
En el comando command
le indicamos a Prometheus que lea la configuración desde el archivo prometheus.yml
que montamos en el volumen, éste es el archivo que creamos en el paso anterior.
Ejecutar nuestra aplicación .NET
Ejecutamos la aplicación y vemos que Prometheus está leyendo los datos que enviamos desde nuestra aplicación.
Dejo el código de ejemplo por acá https://github.com/leomicheloni/prometheus-net-sample
Enjoy.
Blog Bitix
Desempaquetado de altavoz inteligente Amazon Echo (4ª generación)
agosto 31, 2023 11:00
Ya tenía un altavoz Echo de 3ª generación ubicado en la habitación pero me viene bien tener un segundo altavoz en el salón para escuchar música, así que me he hecho con un segundo altavoz Echo en este caso de 4ª generación. Con Amazon Prime y Amazon Music les doy un buen uso. El Echo de 4º tiene algunas diferencias notables para el que las use respecto al de 3ª.
Blog Bitix
Desempaquetado altavoz inteligente Amazon Echo (4ª generación)
agosto 31, 2023 11:00
Ya tenía un altavoz Echo de 3ª generación ubicado en la habitación pero me viene bien tener un segundo altavoz en el salón para escuchar música, así que me he hecho con un segundo altavoz Echo en este caso de 4ª generación. Con Amazon Prime y Amazon Music les doy un buen uso. El Echo de 4º tiene algunas diferencias notables para el que las use respecto al de 3ª.
Blog Bitix
Desempaquetado y análisis de televisión smart tv Samsung 43QN90B
agosto 30, 2023 02:30
Finalmente he comprado una televisión, ahora con Amazon Prime y para ver contenido por streaming es algo que puedo darle el uso que merece. Tras unos días y ver televisión y alguna película la calidad de la Samsung 43QN90B por la que he optado ofrece una calidad de imagen excelente llamándome mucho la atención en la buena calidad de negros. Aunque cualquier cosa mejor de la que venía iba a ser una mejora notable pero su panel VA y el mini led dan una una imagen en calidad significativamente mejor que se agradece en las películas incluso comparando con el panel IPS del monitor BenQ PD2700Q que tengo para el ordenador.
Una sinfonía en C#
Cómo recuperar un branch git en Azure DevOps
agosto 22, 2023 12:00
Introducción
En el post anterior vimos cómo recuperar un repositorio Git que ha sido borrado en Azure Devops. En este caso vamos a ver cómo recuperar un branch que ha sido borrado.
El método fácil
Existe ya un método dentro del portal de Azure DevOps para recuperar un branch borrado. Para ello, vamos a la pestaña Repos y seleccionamos el repositorio donde estaba el branch. Si recordamos el nombre del branch borrado solo tenemos que buscar el branch del siguiente modo:
Buscamos el branch por nombre y vemos que aparece debajo en la lista de branches borrados, haciendo click en los puntos de la derecha podemos restaurarlo mediante la opción Restore. Y voilá, ya tenemos nuestro branch de vuelta.
No sabemos el nombre del branch que fue borrado
En el caso que no recordemos exactamente el nombre no podemos buscarlo, pero podemos mediante git obtener el listado de branches borrados y restaurar el que queramos.
Disclaimer: Este método solo funciona si hicimos checkout del branch en el repo que queremos recuperar el nombre.
Para ello, vamos a nuestro repositorio local y ejecutamos el siguiente comando:
git reflog
Esto nos va a mostrar el listado de commits que hemos hecho en nuestro repositorio local, incluyendo los que hemos hecho en branches que ya no existen.
Con esto vamos a Azure DevOps y buscamos el branch por nombre y lo veremos para recuperarlo igual que en el método anterior.
Enjoy.
Meta-Info
¿Que es?
Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.
Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.
Idea: Juanjo Navarro
Diseño: Albin
Fuentes
- Arragonán
- Bitácora de Javier Gutiérrez Chamorro (Guti)
- Blog Bitix
- Blog de Diego Gómez Deck
- Blog de Federico Varela
- Blog de Julio César Pérez Arques
- Bloggingg
- Buayacorp
- Coding Potions
- DGG
- Es intuitivo...
- Fixed Buffer
- Header Files
- IOKode
- Infectogroovalistic
- Ingenieria de Software / Software Engineering / Project Management / Business Process Management
- Juanjo Navarro
- Koalite
- La luna ilumina por igual a culpables e inocentes
- Made In Flex
- Mal Código
- Mascando Bits
- Metodologías ágiles. De lo racional a la inspiración.
- Navegapolis
- PHP Senior
- Pensamientos ágiles
- Picando Código
- Poesía Binaria
- Preparando SCJP
- Pwned's blog - Desarrollo de Tecnologia
- Rubí Sobre Rieles
- Spejman's Blog
- Thefull
- USANDO C# (C SHARP)
- Una sinfonía en C#
- Variable not found
- Yet Another Programming Weblog
- design-nation.blog/es
- info.xailer.com
- proyectos Ágiles
- psé
- vnsjava