Informática 2

Page 1

Crear proyecto Spring usando Maven Crear proyecto: mvn archetype:generate -DgroupId=com.codemonkey -DartifactId=HolaMundo DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Ahora dentro del directorio (HolaMundo) teclear: mvn eclipse:eclipse

Agregar esto en el pom.xml: <!-- Spring framework --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> <version>2.5.6</version> </dependency>

Teclear de nuevo: mvn eclipse:eclipse Compilar: mvn compile Ejecutar: mvn exec:java -Dexec.mainClass="com.codemonkey.App" Crear JAR: mvn package Ejecutar: java -jar App-0.0.1-SNAPSHOT.jar


pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apac he.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.java2s.common</groupId> <artifactId>Java2sExamples</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>Java2sExamples</name> <url>http://maven.apache.org</url> <dependencies> <!-- <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> --> <!-- Spring framework --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> <version>2.5.6</version> </dependency> </dependencies> </project>

Teclear de nuevo:


mvn eclipse:eclipse

HolaMundo.java package com.codemonkey; public class HolaMundo { private String name;

public void setName(String name) { this.name = name; }

public void printHola() { System.out.println("Spring 3 : Hola! " + name); } }

Crear un archivo XML llamado Spring-Module.xml en src/main/resources <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<bean id="holaBean" class="com.codemonkey.HolaMundo"> <property name="name" value="Fernando" /> </bean>

</beans>

App.java


package com.codemonkey; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "Spring-Module.xml"); HolaMundo obj = (HolaMundo) context.getBean("holaBean"); obj.printHola(); } }

Compilar: mvn compile Ejecutar: mvn exec:java –Dexec.mainClass=”com.codemonkey.App”

Link: http://www.java2s.com/Tutorials/Java/Spring/0030__Spring_HelloWorld.htm

Grails framework $ sdk install grails $ grails –version $ grails grails> créate-app holamundo $ cd holamundo $ grails grails> créate-controller hola package holamundo


class HolaController { def index() { render "La copa de Grails!" } }

grails-app/conf/application.yml Escribir: server: contextPath: /holamundo

grails> run-app

Abrir navegador en: http://localhost:8080/holamundo/ http://localhost:8080/holamundo/index

Links: http://docs.grails.org/latest/guide/single.html

Grails Programmer : How to output CSV from a Grails 3 Controller

1 2 3 4

$ grails -version Grails Version: 3.1.11 | Groovy Version: 2.4.7 | JVM Version: 1.8.0_45


Create an app with the default web profile. 1

grails create-app csv

2

Application created at /Users/groovycalamari/Documents/tests/csv

Enter the grails console 1

$ cd csv

2

$ grais

Create a domain class 1

grails> create-domain-class Book

2

| Created grails-app/domain/csv/Book.groovy

3

| Created src/test/groovy/csv/BookSpec.groovy

Add a couple of properties to the domain class 1

package cvs

2 3

class Book {

4 5

String title

6

String author

7 static constraints = {

8

}

9 10

}


In grails-app/init/BootStrap.groovy add a couple of domain class instances. The BootStrap init closure runs when the app starts. 1

import csv.*

2 3

class BootStrap {

4

def init = { servletContext ->

5

new Book(title: 'Groovy for Domain-Specific Languages', author: 'Fergal Dearle')

6

new Book(title: 'Programming Groovy 2: Dynamic Productivity for the Java Develop Subramaniam').save()

7

}

8

def destroy = {

9

}

10

}

Create a controller 1

$ grails

2

Enter a command name to run. Use TAB for completion:e...

3

grails> create-controller Book

4

| Created grails-app/controllers/csv/BookController.groovy

5

| Created src/test/groovy/csv/BookControllerSpec.groovy

This is the controller content: 1

package csv

2 import grails.config.Config

3

import grails.core.support.GrailsConfigurationAware

4 5 6

import static org.springframework.http.HttpStatus.OK


7

class BookController implements GrailsConfigurationAware {

8 9

String csvMimeType

10 11

String encoding

12 13

def index() { final String filename = 'book.csv'

14 15

def lines = Book.findAll().collect { [it.title, it.author].join(';') } as List<String>

16 17

def outs = response.outputStream

18

response.status = OK.value()

19

response.contentType = "${csvMimeType};charset=${encoding}";

20

response.setHeader "Content-disposition", "attachment;

21

filename=${filename}"

22 lines.each { String line ->

23

outs << "${line}\n"

24 }

25 26

outs.flush()

27 28

outs.close() }

29 30

@Override

31

void setConfiguration(Config co) {

32 33 34

csvMimeType = co.getProperty('grails.mime.types.csv', String, 'text/csv') encoding = co.getProperty('grails.converters.encoding', String, 'UTF-8')


}

35 36

}

37

Several things about the above code. A) I will recommend to put the logic fetching the lines in a Service. B) I am using the mime type and encoding defined in application.yml. Learn more about retrieving config values. C) If you want the file to download you need to setup the Contentdisposition header. If we run the app and call the controller we will download a CSV file as this:

CSV is probably the best format to export your data from a Grails App. A CSV file is easy to import in Excel. I was tired of my clients asking me how to import a CSV in Excel. I wrote a post; in Spanish though. Do you like to read about Grails / Groovy development? If the answer is yes, subscribe to Groovy Calamari. A weekly curated email newsletter about the Groovy ecosystem. Links: http://sergiodelamo.es/grails-tips-how-to-output-csv-from-a-grails-3-controller/


¿Cómo importar un archivo CSV en Excel? Las siguientes capturas de pantalla muestra como importar un archivo CSV en Excel 2010. CSV es el acrónimo en inglés de Comma Separated Value. En español se suele utilizar el termino archivo separado por comas. 1. El primer paso es descargar el archivo CSV en un directorio fácilmente localizable de nuestro ordenador. 2. Ejecutar Excel 3. Crear un documento Nuevo. 4. Pulsar la pestaña Datos y luego el botón “Obtener Datos Externos -> Desde Texto”. 5. En el campo “Origen del archivo” se pide la codificación del mismo. Lo habitual es que sea UTF8. 6. Pulsar “Siguiente”.


7. En la siguiente pantalla nos piden que establezcamos los separadores que se usan para delimitar los datos. Lo habitual es que los datos vengan separados por coma por lo que tendremos que marcar la opción “Coma”. 8. Pulsar Siguiente


En la siguiente pantalla se nos da la opción de designar un formato para cada columna que se va a importar a Excel. Dependiendo del uso que le vayamos a dar a los datos quizás nos interese delimitar alguna columna como valor numérico o quizás el valor “Genérico por defecto sea suficiente”.


Al pulsar finalizar los datos se importarĂ­an a nuestra hoja de cĂĄlculo

Spring Data JPA - Pagination With Thymeleaf View [Updated: Jun 12, 2018, Created: Jun 12, 2018]

In the last tutorial we saw how to use Pageable parameter in Spring MVC controller methods. Following example shows how to use Spring Data pagination with Thymeleaf view. We are also going to use @PageableDefault


annotation to change the default page size.

Example Entity @Entity public class Employee { private @Id @GeneratedValue Long id; private String name; private String dept; private int salary; ............. }

Repository public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> { }

JavaConfig @ComponentScan @Configuration @EnableWebMvc @EnableJpaRepositories @EnableSpringDataWebSupport public class AppConfig {

@Bean


public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); factory.setPersistenceProviderClass(HibernatePersistenceProvider.class); return factory; }

@Bean public PlatformTransactionManager transactionManager( EntityManagerFactory entityManagerFactory) { JpaTransactionManager txManager = new JpaTransactionManager(); txManager.setEntityManagerFactory(entityManagerFactory); return txManager; }

//thymeleaf view configurations @Autowired ApplicationContext applicationContext;

@Bean public SpringTemplateEngine templateEngine() { SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver()); return templateEngine; }

@Bean public SpringResourceTemplateResolver templateResolver() { SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(this.applicationContext); templateResolver.setPrefix("/WEB-INF/views/");


templateResolver.setSuffix(".html"); return templateResolver; }

@Bean public ViewResolver viewResolver() { ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); viewResolver.setTemplateEngine(templateEngine()); return viewResolver; } }

Spring MVC controller @Controller public class EmployeeController {

@Autowired private EmployeeRepository repository;

@GetMapping("/employees") public String getEmployees(@PageableDefault(size = 10) Pageable pageable, Model model) { Page<Employee> page = repository.findAll(pageable); model.addAttribute("page", page); return "employee-page"; } }


Thymeleaf view src/main/webapp/WEB-INF/views/employee-page.html <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <style> table{width:100%;} table td, table th { border: 1px solid grey;} table th { background: #eee;} .pagination-div{user-select: none;} .pagination-div span{border-radius:3px;border:1px solid #999; padding:5px;margin:10px 0px 0px 10px;display:inline-block} span.selected{background:#ccf;} </style> </head> <body> <h2>Employees</h2> <table> <tr><th>Id</th> <th>Name</th> <th>Department</th> <th>Salary</th> </tr> <tr th:each="employee : ${page.content}"> <td th:text="${employee.id}"></td> <td th:text="${employee.name}"></td> <td th:text="${employee.dept}"></td> <td th:text="${employee.salary}"></td> </tr>


</table>

<div class="pagination-div"> <span th:if="${page.hasPrevious()}"> <a th:href="@{/employees(page=${page.number-1},size=${page.size})}"> Previous</a> </span> <th:block th:each="i: ${#numbers.sequence(0, page.totalPages - 1)}"> <span th:if="${page.number == i}" class="selected">[[${i}+1]]</span> <span th:unless="${page.number == i}"> <a th:href="@{/employees(page=${i},size=${page.size})}">[[${i}+1]]</a> </span> </th:block> <span th:if="${page.hasNext()}"> <a th:href="@{/employees(page=${page.number+1},size=${page.size})}">Next</a> </span> </div> </body> </html>

Running To try examples, run embedded tomcat (configured in pom.xml of example project below):

mvn tomcat7:run-war

Output localhost:8080/employees


Navigating to pages by clicking page links:


Example Project Dependencies and Technologies Used: 

spring-data-jpa 2.0.7.RELEASE: Spring Data module for JPA repositories. Uses org.springframework:spring-context version 5.0.6.RELEASE

spring-webmvc 5.0.6.RELEASE: Spring Web MVC.

javax.servlet-api 3.0.1 Java Servlet API

hibernate-core 5.3.1.Final: Hibernate's core ORM functionality. Implements javax.persistence:javax.persistence-api version 2.2

h2 1.4.197: H2 Database Engine.

thymeleaf-spring5 3.0.9.RELEASE: Modern server-side Java template engine for both we


jackson-databind 2.9.5: General data-binding functionality for Jackson: works on core st

JDK 1.8

Maven 3.3.9

https://www.logicbig.com/tutorials/spring-framework/spring-data/pagination-withthymeleaf.html Descarga: https://www.logicbig.com/tutorials/spring-framework/spring-data/pagination-withthymeleaf/spring-data-jpa-pagination-with-thymeleaf.zip

Tutorial sobre control de versiones (parte 1) En el sitio de Software Carpentry hay un buen tutorial sobre el uso de sistemas de control de versiones. Estos apuntes son prácticamente la traducción de esas instrucciones.

Indice del Tutorial o o o o

Parte 1. Cómo armar un repositorio local Parte 2. Cómo subir el repositorio local al remoto Parte 3. Cómo colaborar en un mismo repositorio remoto Parte 4. Cómo resolver conflictos

Para ver las versiones (en inglés) en las cuales se basa este tutorial, podés visitar la página de Software Carpentry


Configuración La primera vez que se usa Git en una máquina hay que configurar al menos el nombre del usuario y su email. También puede elegirse el editor por defecto. Son comandos que se ejecutan una sola vez. Por ejemplo: $ git config --global user.name "Vlad Dracula" $ git config --global user.email "vlad@tran.sylvan.ia" $ git config --global color.ui "auto" $ git config --global core.editor "emacs"

La tercer opcion es para que los mensajes con los cuales responde Git en el terminal estén coloreados. La cláusula “—global” indica que esta configuración no se limita a un proyecto particular, sino que será utilizado en todos los proyectos (repositorios) de esa computadora. Para verificar el nombre de usuario se usa git config user.name.

Inicialización Se puede comenzar a trabajar con un proyecto usando el siguiente comando desde el subdirectorio en el cual se alojarán los archivos del proyecto: $ git init

De esta manera se crea un subdirectorio oculto (.git) donde se irá alojando todo el historial de archivos, en todas sus versiones, a medida que se vayan produciendo.

Agregar un archivo al proyecto Creamos un archivo de texto con cualquier editor (no necesariamente el elegido en la configuración) y lo almacenamos en el directorio del proyecto. Por ejemplo una canción de La Vela Puerca, en el archivo va-a-escampar.txt:


Va a escampar

Hoy asume lo que venga sea para bien, o todo mal. y aunque pierda lo que tenga se va a morder para aguantar

Si hacemos “git status” el sistema nos responde que reconoce la existencia de un nuevo archivo en el proyecto, que todavía el sistema de control de versiones no incorporó. $ git status # On branch master # # Initial commit # # Untracked files: #

(use "git add <file>..." to include in what will be committed)

# #

va-a-escampar.txt

nothing added to commit but untracked files present (use "git add" to track)

Para agregar el archivo al proyecto se ejecuta: $ git add va-a-escampar.txt

Y se puede pedir el nuevo informe de situación con: $ git status # On branch master # # Initial commit #


# Changes to be committed: #

(use "git rm --cached <file>..." to unstage)

# #

new file:

va-a-escampar.txt

#

Comitear es un verbo muy extraño Ahora Git ya sabe que debe ir registrando la evolución del archivo va-a-escampar.txt, pero todavía no ha grabado ninguna información sobre este archivo en su base de datos (y si ya hubiese información de antes, aún no habría guardado los cambios realizados desde el último commit). En inglés, grabar el estado actual de uno o más archivos es hacer un “commit”. En castellano commit puede traducirse como cometer, hacer, encomendar, perpetrar… Ninguna de las cuales sirve para describir lo que sucede, así que hablaremos de “comitear”. Para comitear el estado actual del archivo se ejecuta la orden git commit -m “mensaje personal”: $ git commit -m "Empezando a transcribir la letra de La Vela" [master (root-commit) 38b86f5] Empezando a transcribir la letra de La Vela 1 file changed, 6 insertions(+) create mode 100644 va-a-escampar.txt

Git guarda entonces un copia permanente de todos los archivos que están en la base de datos (los que fueron agregados con “git add”) dentro del directorio oculto .git. Esta copia permanente es llamada una revisión, y su identificador en el ejemplo fue 38b86f5. De omitir el mensaje en el comando git commit (si no hubiese aparecido -m “mensaje”), se hubiese abierto el editor configurado al principio, para poder escribir el mensaje. Volvemos a ver la situación actual del proyecto con “git status” y con “git log” podemos ver el historial de cambios (en orden cronológico inverso), el cual incluye el identificador completo de la revisión, el autor, la fecha y el mensaje de Git al responder al commit:


$ git status # On branch master nothing to commit, working directory clean

$ git log commit 38b86f5625453732e442c127f1d4678ec8550a12 Author: eldiegoefe <eldiegoefe@gmail.com> Date:

Mon Oct 6 15:35:07 2014 -0300

Empezando a transcribir la letra de La Vela

Cambios (o agregados) en un archivo ¿Dónde se guardan los cambios? En el directorio del proyecto sigue habiendo un solo archivo. Toda la información extra se almacena en el subdirectorio oculto .git, de modo que el sistema de archivos se ve limpio y se evita la posibilidad de borrar accidentalmente cosas (como versiones viejas del mismo archivo). Si agregamos una segunda estrofa al archivo de texto, pasa a verse así: Va a escampar

Hoy asume lo que venga sea para bien, o todo mal. y aunque pierda lo que tenga se va a morder para aguantar

Hoy que claro ve las cosas que ayer no vio, ni va a exigir


Sobre su pena se posa quiere entender para seguir

Al pedir el status del proyecto veremos: $ git status # On branch master # Changes not staged for commit: #

(use "git add <file>..." to update what will be committed)

#

(use "git checkout -- <file>..." to discard changes in working directory)

# #

modified:

va-a-escampar.txt

# no changes added to commit (use "git add" and/or "git commit -a")

La última linea es la frase clave: “no se agregaron cambios al commit”. El archivo ha cambiado pero no le hemos dicho aún a Git que queremos guardar esos cambios (lo haremos con git add).

Comparación de versiones Antes de agregar estos cambios podemos revisar nuestro trabajo usando git diff, que nos muestra las diferencias entre el estado actual del archivo y la última versión comiteada. $ git diff diff --git a/va-a-escampar.txt b/va-a-escampar.txt index 97ab7b0..db818ec 100644 --- a/va-a-escampar.txt +++ b/va-a-escampar.txt @@ -4,3 +4,8 @@ Hoy asume lo que venga sea para bien, o todo mal. y aunque pierda lo que tenga


se va a morder para aguantar + +Hoy que claro ve las cosas +que ayer no vio, ni va a exigir +Sobre su pena se posa +quiere entender para seguir

La salida es críptica porque es en realidad una serie de comandos para que un editor de textos pueda reconstruir un archivo a partir del otro. 1.

La primera linea muestra que Git produce una salida similar al comando Unix diff comparando la versión antigua y nueva del archivo. 2. La segunda linea muestra la revisión exacta de cada archivo que está siendo comparado (97ab7b0 y db818ec, etiquetas únicas generadas por la computadora para esas revisiones). 3. La linea restante muestra finalmente las lineas en las que aparecen las diferencias (marcando con “+” las lineas que se agregaron).

Cómo comitear los cambios Si hacemos un commit de los cambios: $ git commit -m "Agregada la segunda estrofa" # On branch master # Changes not staged for commit: #

(use "git add <file>..." to update what will be committed)

#

(use "git checkout -- <file>..." to discard changes in working directory)

# #

modified:

va-a-escampar.txt

# no changes added to commit (use "git add" and/or "git commit -a")

Ups. No se comitearon los cambios porque faltó agregarlos antes. Entonces, haremos:


$ git add va-a-escampar.txt $ git commit -m "Agregada la segunda estrofa" [master 6ae57e3] Agregada la segunda estrofa 1 file changed, 5 insertions(+)

Git insiste en que agreguemos los archivos al conjunto de los que querramos comitear antes de realmente hacerlo, porque quizás no querramos comitear todo junto. Por ejemplo, supongamos que estamos agregando algunas citas a un documento. Podríamos querer agregarlas sin comitear el trabajo que estamos haciendo en las conclusiones (que todavía no terminamos). NOTA PERSONAL: ¡no entiendo! ¿De qué me sirve agregar los cambios si no los “comiteo”? RESPUESTA: Para permitirlo, Git tiene una zona de almacenamiento especial donde hace un seguimiento de cosas que fueron agregadas al último conjunto de cambios, pero que aún no fueron comiteadas. Con git add se colocan las novedades en este area, y luego git commit las copia al espacio de almacenamiento de largo plazo. Veamos cómo nuestros cambios a un archivo se mueven desde nuestro editor hacia la staging area(zona de preparación o también area de ensayo) y hacia el almacenamiento de largo plazo. Primero, agregamos otra estrofa: Llega la batalla y contra él estalla algún día va a escampar. y como sale de esta quiere la respuesta sabe que no es escapar.

Y analizamos las diferencias con git diff: $ git diff diff --git a/va-a-escampar.txt b/va-a-escampar.txt index db818ec..0c33091 100644


--- a/va-a-escampar.txt +++ b/va-a-escampar.txt @@ -9,3 +9,10 @@ Hoy que claro ve las cosas que ayer no vio, ni va a exigir Sobre su pena se posa quiere entender para seguir + +Llega la batalla +y contra él estalla +algún día va a escampar. +y como sale de esta +quiere la respuesta +sabe que no es escapar.

Git identifica las diferencias entre el archivo y la versión intermedia (pero no comiteada), guardada en la staging area (la voy a llamar zona de preparación). Si agregamos estos cambios al almacenamiento intermedio, veremos lo siguiente: $ git add va-a-escampar.txt $ git diff

Ahora no hay ninguna salida, porque el archivo actualmente en edición es igual al que guardamos en la zona de preparación. Sin embargo, si hacemos: $ git diff --staged diff --git a/va-a-escampar.txt b/va-a-escampar.txt index db818ec..0c33091 100644 --- a/va-a-escampar.txt +++ b/va-a-escampar.txt @@ -9,3 +9,10 @@ Hoy que claro ve las cosas


que ayer no vio, ni va a exigir Sobre su pena se posa quiere entender para seguir + +Llega la batalla +y contra él estalla +algún día va a escampar. +y como sale de esta +quiere la respuesta +sabe que no es escapar.

Ahora nos está mostrando las diferencias entre el último cambio comiteado (en el almacenamiento de largo plazo) y lo que hay en la zona de preparación. Guardemos estos cambios: $ git commit -m "Tercera estrofa agregada" [master 8f1eec1] Tercera estrofa agregada 1 file changed, 7 insertions(+)

Vemos cómo quedó: $ git status # On branch master nothing to commit, working directory clean

Y podemos examinar la historia de lo que fue sucediendo hasta ahora: $ git log commit 8f1eec1836a9ace8a2cbab7e2c3341efa5c3a537 Author: eldiegoefe <eldiegoefe@gmail.com> Date:

Mon Oct 6 19:55:10 2014 -0300


Tercera estrofa agregada

commit 6ae57e3d91a7a526a257df081d83a5b9be4e6d28 Author: eldiegoefe <eldiegoefe@gmail.com> Date:

Mon Oct 6 16:54:40 2014 -0300

Agregada la segunda estrofa

commit 38b86f5625453732e442c127f1d4678ec8550a12 Author: eldiegoefe <eldiegoefe@gmail.com> Date:

Mon Oct 6 15:35:07 2014 -0300

Empezando a transcribir la letra de La Vela

Resumiendo, cuando queremos hacer cambios en nuestro repositorio, primero tenemos que agregar los cambios a la zona de preparación (con git add), y luego comitear los cambios ensayados al repositorio (con git commit).

Explorando el historial Para ver lo que cambiamos, usamos git diff también, pero para refiriéndonos a versiones viejas con la notación HEAD~1, HEAD~2, etc: $ git diff HEAD~1 va-a-escampar.txt diff --git a/va-a-escampar.txt b/va-a-escampar.txt index db818ec..0c33091 100644 --- a/va-a-escampar.txt +++ b/va-a-escampar.txt @@ -9,3 +9,10 @@ Hoy que claro ve las cosas que ayer no vio, ni va a exigir Sobre su pena se posa


quiere entender para seguir + +Llega la batalla +y contra él estalla +algún día va a escampar. +y como sale de esta +quiere la respuesta +sabe que no es escapar. $ git diff HEAD~2 va-a-escampar.txt diff --git a/va-a-escampar.txt b/va-a-escampar.txt index 97ab7b0..0c33091 100644 --- a/va-a-escampar.txt +++ b/va-a-escampar.txt @@ -4,3 +4,15 @@ Hoy asume lo que venga sea para bien, o todo mal. y aunque pierda lo que tenga se va a morder para aguantar + +Hoy que claro ve las cosas +que ayer no vio, ni va a exigir +Sobre su pena se posa +quiere entender para seguir + +Llega la batalla +y contra él estalla +algún día va a escampar. +y como sale de esta +quiere la respuesta +sabe que no es escapar.


De esta manera construimos una cadena de revisiones. El extremo más reciente de la cadena es HEAD; podemos referirnos a revisiones previas usando la notación ~, de manera que HEAD~1 (se pronuncia “head menos uno”) significa “la revisión previa”, mientras que HEAD~123 vuelve 123 revisiones hacia atrás, desde donde nos encontramos en la actualidad. También nos podemos referir a las revisiones usando las cadenas largas de dígitos y letras que Git muestra en los logs. Estos son IDs únicos para los cambios, “únicos” significa realmente únicos: cada cambio a cualquier conjunto de archivos en cualquier máquina tiene un identificador de 40 caracteres. Nuestro primer commit nos dio el ID 38b86f5625453732e442c127f1d4678ec8550a12, así que probemos esto: $ git diff 38b86f5625453732e442c127f1d4678ec8550a12 va-a-escampar.txt diff --git a/va-a-escampar.txt b/va-a-escampar.txt index 97ab7b0..0c33091 100644 --- a/va-a-escampar.txt +++ b/va-a-escampar.txt @@ -4,3 +4,15 @@ Hoy asume lo que venga sea para bien, o todo mal. y aunque pierda lo que tenga se va a morder para aguantar + +Hoy que claro ve las cosas +que ayer no vio, ni va a exigir +Sobre su pena se posa +quiere entender para seguir + +Llega la batalla +y contra él estalla +algún día va a escampar. +y como sale de esta


+quiere la respuesta +sabe que no es escapar.

Para no tipear cadenas de 40 números, Git permite usar los primeros de la cadena, con el mismo resultado: $ git diff 38b86f5 va-a-escampar.txt

Recuperar versiones antiguas De acuerdo: podemos grabar cambios a los archivos y ver qué hemos cambiado. Pero ¿cómo restauramos versiones viejas de las cosas? Supongamos que sobreescribimos accidentalmente nuestro archivo va-a-escampar.txt, que pasa a contener sólo la siguiente linea: no va a escampar nada

El comando cat muestra el contenido del archivo: $ cat va-a-escampar.txt no va a escampar nada

Con git status nos enteramos que el archivo ha cambiado, pero esos cambios aún no pasaron a la zona de preparación (staging area). $ git status # On branch master # Changes not staged for commit: #

(use "git add <file>..." to update what will be committed)

#

(use "git checkout -- <file>..." to discard changes in working directory)

# #

modified:

va-a-escampar.txt

# no changes added to commit (use "git add" and/or "git commit -a")


Podemos volver atrás, dejando las cosas como estaban antes, usando git checkout: $ git checkout HEAD va-a-escampar.txt $ cat va-a-escampar.txt

Va a escampar

Hoy asume lo que venga sea para bien, o todo mal. y aunque pierda lo que tenga se va a morder para aguantar

Hoy que claro ve las cosas que ayer no vio, ni va a exigir Sobre su pena se posa quiere entender para seguir

Llega la batalla y contra él estalla algún día va a escampar. y como sale de esta quiere la respuesta sabe que no es escapar.

Con git checkout se recupera una versión anterior de un archivo. En este caso, le estamos pidiendo a Git que recupere la versión del archivo guardada en HEAD, que fue la última revisión guardada. Si quisiéramos ir más atrás podríamos usar un identificador de revisión: $ git checkout 38b86f5 va-a-escampar.txt


Es importante recordar que debemos usar el número de revisión que identifica el estado del repositorio antes del cambio que estamos tratando de revertir. Un error común es usar el número de revisión del commit en el cual hicimos el cambio del cual estamos tratando de deshacernos. En el ejemplo de abajo queremos recobrar el estado inmediatamente anterior al commit más reciente (HEAD~1), que es la revisión 6ae57e3d9 (en la figura -realizada con otro ejemplo- corresponde a la revision f22b25e): El diagrama siguiento ilustra el modo en que puede verse la historia de un archivo (moviéndose hacia atrás desde HEAD, la versión más recientemente comiteada): Simplificando un caso común

Si mirás cuidadosamente la salida de *git status*, vas a ver esta ayuda:

(use "git checkout -- <file>..." to discard changes in working directory)

Tal como afirma, *git checkout* sin un identificador de versión restaura los archivos al estado guardado en HEAD. El guión doble -- es necesario para separar los nombres de los archivos siendo recuperados del comando mismo: sin esos guiones, Git trataría de usar el nombre del archivo como identificador de la revisión a la cual se desea volver.

El hecho de que los archivos puedan recuperarse o revertirse uno por uno tiende a cambiar el modo en que la gente organiza su trabajo. Si todo estuviese en un solo gran archivo, entonces es dificil (si no imposible) deshacer los cambios a la introducción sin también deshacer los cambios hechos a continuación a la conclusión. Por otra parte, si la introducción y la conclusión estuviesen en archivos separados, entonces sería más fácil moverse hacia atrás y hacia adelante en el tiempo.


Ignorando cosas Vamos a ver qué hacer con los archivos del mismo directorio que no queremos incluir en el sistema de control de versiones. Agregamos algunos archivos vacíos: a.dat $ mkdir results $ touch a.dat b.dat c.dat results/a.out results/b.out

Y vemos qué dice Git: $ git status # On branch master # Untracked files: #

(use "git add <file>..." to include in what will be committed)

# #

a.dat

#

b.dat

#

c.dat

#

results/

nothing added to commit but untracked files present (use "git add" to track)

Colocar estos archivos bajo control de versiones sería un desperdicio de espacio en disco. Peor aún, tenerlos listados nos distraería de los cambios que realmente importan, de manera que debemos decirle a Git que los ignore, creando un archivo en el directorio raiz del proyecto. Al archivo lo llamamos .gitignore. Dentro de .gitignore colocamos los patrones para ignorar archivos, en este caso los archivos

que

terminen

en .dat y

los

archivos

que

se

encuentren

en

el

subdirectorio results (si cualquiera de estos archivos hubiese estado siendo parte del control de versiones, Git continuará considerándolo a pesar de figurar en .gitignore). Al listar el contenido de .gitignore deberíamos obtener esto:


$ cat .gitignore *.dat results/

Si pedimos el status del proyecto, la salida se verá mucho más limpia que antes: $ git status # On branch master # Untracked files: #

(use "git add <file>..." to include in what will be committed)

# #

.gitignore

nothing added to commit but untracked files present (use "git add" to track)

La única cosa que Git advierte ahora es el archivo .gitignore recientemente creado. Podríamos creer que no querríamos incorporarlo al control de versiones, pero todos con quienes compartimos el repositorio probablemente querrían ignorar las mismas cosas que estamos ignorando nosotros. Así que agregamos y comiteamos este archivo (oculto, pues empieza con un punto): $ git add .gitignore $ git commit -m "Agregado del archivo de ignorancias" $ git status # On branch master nothing to commit, working directory clean

Como un extra, al usar .gitignore evitamos agregar accidentalmente archivos que no queremos al repositorio. $ git add a.dat The following paths are ignored by one of your .gitignore files: a.dat Use -f if you really want to add them.


fatal: no files added

Si queremos realmente pasar por alto los seteos de .gitignore, podemos usar git add f para forzar a Git a agregar algo. Siempre podemos ver la situación de los archivos ignorados si quisiéramos: $ git status --ignored

# On branch master # Ignored files: #

(use "git add -f <file>..." to include in what will be committed)

# #

a.dat

#

b.dat

#

c.dat

#

results/

nothing to commit, working directory clean

Claves Usar git config para configurar un usuario, dirección de email, editor y otras preferencias (todas estas cosas son válidas para una máquina) Con git init se inicializa un repositorio Con git status se muestra la situación de un repositorio Los archivos puedes almacenarse en el directorio de trabajo (que los usuarios ven), la zona de preparación o staging area (desde donde se realizará el próximo commit) y el repositorio local (donde las instantáneas son almacenadas permanentemente).


Con git add se agregan archivos a la zona de preparación. Con git commit se crea una instantanea de la zona de preparación en el repositorio local. Siempre escribir un mensaje al comitear cambios (con git commit -m “mensaje”). Con git diff se muestran las diferencias entre versiones. Con git checkout se recuperan viejas versiones de un archivo. El archivo .gitignore indica a Git los archivos a ser ignorados por el sistema de control de versiones. No te vayas sin comentar algo del post, de las noticias del día, del clima, que se yo...

Tutorial sobre control de versiones (parte 2) Indice del Tutorial o o o o

Parte 1. Cómo armar un repositorio local Parte 2. Cómo subir el repositorio local al remoto Parte 3. Cómo colaborar en un mismo repositorio remoto Parte 4. Cómo resolver conflictos

Para ver las versiones (en inglés) en las cuales se basa este tutorial, podés visitar la página de Software Carpentry


Armar un proyecto (repositorio) en GitHub Para subir el repositorio local a GitHub es indispensable tener una cuenta allí (que es gratis, así que a relajarse y disfrutar). Es bastante intuitivo el modo de crear un repositorio. Pero pongo un par de imágenes, a modo de muestra.



En la pantalla anterior hay que hacer click sobre el botón verde que dice “+ New repository“, que nos deposita en la imagen de abajo.

Solamente ponemos el nombre y la descripción del repositorio. Los repositorios privados son pagos, así que por lo general uno elige “Public“. No hace falta inicializarlo con un readme, ni agregar un.gitignore, ni seleccionar una licencia. Basta con apretar el botón verde de “Create repository” y chan, ya tenemos repositorio sin tener que acercarnos a la farmacia. La imagen que sigue es para mostrar que la dirección del nuevo repositorio, aún vacío, pero listo para clonarse sin parecerse a la oveja Dolly, se encuentra sobre el panel de la izquierda, donde dice “HTTPSclone URL”. Copiamos de ahí esa dirección y nos dirigimos con presteza y gráciles movimientos al terminal, que debería estar ubicado


(controlar con pwd) en el directorio de nuestro repositorio local. Escribimos (en mi caso estoy trabajando sobre un nuevo repositorio para alojar la configuración de mi Emacs):

Setear el repositorio remoto desde nuestro repo local En la siguiente orden le estamos diciendo a Git que agregue como repositorio remoto la URL que le pasamos, y además a esa dirección le asignamos el alias “origin” (se suele utilizar origin por convención, pero podríamos haberlo llamado “farodelfindelmundo“). Podemos chequearlo con git remote -v.


$ git remote add origin https://github.com/eldiegoefe/emacs.git $ git remote -v origin

https://github.com/eldiegoefe/emacs.git (fetch)

origin

https://github.com/eldiegoefe/emacs.git (push)

Subir repo local al remoto Ahora es superfacil subir los archivos de nuestro repositorio local hacia el repositorio remoto (en github.com): $ git push origin master

Cuando te dicen “master” es porque sos grosso, sabelo. Además “master” es la única rama (branch) que por el momento tenemos en el recientemente creado repositorio remoto y vacío (se me cae un lagrimón). También es la única rama que tenemos en el repo local (lo podemos chequear con git branch). Al ejecutar el git push el sistema les va a pedir primero el nombre de usuario y luego la contraseña (que se hicieron en github, porque hacia allí está subiendo los archivos).

¿Problemas? Si acaso diera un error, como me sucedió en el ejemplo que les estoy relatando, es porque el repositorio remoto en vez de estar vacío tiene algún contenido. Debemos entonces, antes de subir cosas, bajar ese contenido que no tenemos en el repositorio local (en mi caso el archivo con la licencia), con la orden “git pull” (git push empuja desde el local hacia el remoto, git pull tira desde el remoto hacia el local): $ git pull origin master warning: no common commits remote: Counting objects: 3, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done.


From https://github.com/eldiegoefe/emacs * branch

master

-> FETCH_HEAD

* [new branch]

master

-> origin/master

Merge made by the 'recursive' strategy. LICENSE | 675 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++ 1 file changed, 675 insertions(+) create mode 100644 LICENSE

Cuando se agregan cosas (al repo local) hay que comitearlas, como de costumbre. Por eso, al ejecutar el comando anterior el sistema pide un mensaje de “commit” y luego da el resultado que muestro (por ahora es un jeroglífico, pero tiene sentido, eh). En mi caso, el archivo LICENCE que sólo estaba en el remoto, ahora está también en el local, lo cual puede verse haciendo: $ ls custom.el

LICENSE

preload

Podemos chequear el estado del repo local: $ git status # On branch master nothing to commit, working directory clean

No problem Ahora sí volvemos a intentar subir los archivos que faltan hacia origin (el repo remoto, solo, triste y abandonado): $ git push origin master Counting objects: 12, done. Delta compression using up to 4 threads. Compressing objects: 100% (8/8), done. Writing objects: 100% (11/11), 2.88 KiB | 0 bytes/s, done.


Total 11 (delta 2), reused 0 (delta 0) To https://github.com/eldiegoefe/emacs.git e23c676..2c817b6

master -> master

“Joyines”, diría el Tano. Si ahora hiciésemos un pull desde el remoto, no debería pasar nada porque ambos repos son iguales. Veamos: git pull origin master From https://github.com/eldiegoefe/emacs * branch

master

-> FETCH_HEAD

Already up-to-date.

En cambio, si alguien actualizó el remoto con algún archivo nuevo, o modificó los existentes, al hacer el pull se incorporarían al repo local esos cambios (lo cual siempre hay que hacer antes de hacer un push hacia el remoto). La bandera '-u'

Es común encontrar que git push se acompañe de la bandera '-u'. Vamos a dejar eso para después, pero es casi seguro que se usa para fijar origen y destino, de manera que un git push desnudo, funcionará después con las ramas origen y destino que acompañaban al -u (que es algo de --upstream nosecuanto). Pero estoy tocando de oido, mejor veamos más adelante.

Nos vemos en la parte 3.

No te vayas sin comentar algo del post, de las noticias del día, del clima, que se yo..


Tutorial sobre control de versiones (parte 3) Indice del Tutorial o o o o

Parte 1. Cómo armar un repositorio local Parte 2. Cómo subir el repositorio local al remoto Parte 3. Cómo colaborar en un mismo repositorio remoto Parte 4. Cómo resolver conflictos

Para ver las versiones (en inglés) en las cuales se basa este tutorial, podés visitar la página de Software Carpentry

Probando el cooperativismo Vamos a practicar cómo se realiza una colaboración a través de un repositorio en Github. Para ello nada mejor que colaborar con uno mismo. Lo que hago es trabajar con dos cuentas en Github, corriendo un usuario en mi PC de escritorio y el otro dentro de una máquina virtual dentro de la misma PC (aunque también lo podría hacer desde una notebook, o desde otra sesión de esta misma computadora, pero decir que uno corre una máquina virtual con Linux suena mucho más god-level). No voy a detenerme a explicar nada sobre las máquinas virtuales porque además de ser demasiado sencillo también es off-topic, y no quiero offtopiquearme. ¡Cómprense un amiguito y chau! Vamos a seguir haciendo las pruebas usando el repositorio emacs con el que estaba nerdeando/tinkereando/boludeando (ahhhh, la flexibilidad del lenguaje) en la parte 2. Voy a darle permiso de acceso (no-carnal) a un tercero (bioingenieroDiegol, mi otra cuenta). Con este propósito me dirijo a la página del repositorio en Github, y selecciono Settings/Collaborators. Una vez allí agrego a bioingenieroDiegol.


Clonación Luego me cambio a la PC virtual y allí ya vestido como bioingenieroDiegol voy a clonar el repositorio en esa virtuosa computadora: [bioingenierodiegol]$ git clone https://github.com/eldiegoefe/emacs.git [bioingenierodiegol]$ cd emacs [bioingenierodiegol]$ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working directory clean

Ahora tengo los mismos archivos en la compu de bioingenieroDiegol que en el repositorio de Github. Además sucede algo muy curioso, sin haber hecho git remote add origin https://github.com/eldiegoefe/emacs.git vamos a tener: [bioingenierodiegol]$ git remote -v origin

https://github.com/eldiegoefe/emacs.git (fetch)

origin

https://github.com/eldiegoefe/emacs.git (push)

Vemos que la url y el alias estaban en el repositorio. Es por convención que se usa el nombre de origincomo alias. Cuando más adelante quiera hacer el git push voy a usar directamente git push origin master y va a funcionar.

Cambios hechos por el colaborador A

continuación

hago

algunos

cambios

en

los

archivos

locales

de bioingenieroDiegol (para esta prueba puedo agregar un archivo nuevo o modificar los existentes) y luego voy a tratar de subirlos al repo remoto (que sigue siendo propiedad de eldiegoefe). Para esta última tarea es que hube agregado (¡caramba!) a bioingenieroDiegol a la lista de colaboradores (porque para la clonación no necesitaba permisos).


[bioingenierodiegol]$ touch archivo-vacio.txt [bioingenierodiegol]$ git add archivo-vacio.txt [bioingenierodiegol]$ git commit -m "Agrego archivo-vacio.txt" [bioingenierodiegol]$ git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working directory clean

Git es muy comunicativo y nos dice lo que sucede y cómo tenemos que continuar. Nuestra rama masterlocal está adelantada respecto de la rama master que se encuentra en origin (origin/master) por 1 commit (el que acabamos de realizar).

El colaborador sube sus cambios a Github Tenemos que hacer un push para llevar los cambios locales de bioingenieroDiegol al remoto. No hay nada que comitear (ni nada que festejar, aún). Vamos a hacerle caso a Git, vean lo que sucede tras ungit push origin master hecho por bioingenieroDiegol (no se confundan con el prompt de esa consola, que es manjaro-efe%):

Ningún error, todo bien (vean que tuve que responder con el username y password del colaborador bioingenieroDiegol, bah, el password no se ve…).


El dueño original del repositorio actualiza su repo local Finalmente el dueño original del repositorio (yo, eldiegoefe) quiere actualizar su repositorio local con los cambios hechos por todos los contribuyentes (en este caso sólo bioingenieroDiegol). Para ello hace un pull desde el remoto: [eldiegoefe]$ git pull origin master remote: Counting objects: 2, done. remote: Compressing objects: 100% (1/1), done. remote: Total 2 (delta 1), reused 2 (delta 1) Unpacking objects: 100% (2/2), done. From https://github.com/eldiegoefe/emacs * branch 2c817b6..f94cdfa

master

-> FETCH_HEAD

master

-> origin/master

Updating 2c817b6..f94cdfa Fast-forward archivo-vacio.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 archivo-vacio.txt

El sistema le indica que se actualizó, que un archivo cambió (insertions y deletions son cambios en el interior de los archivos, como no hubo ningun cambio entonces quedan en cero). El método por el cual se hizo la fusión (merge) entre el remoto y el local fue Fast-forward y no hubo ningún conflicto. ¡Fiesta!

Claves Un repositorio local Git puede estar conectado a uno o más repositorios remotos.


Se puede usar el protocolo HTTPS para conectarse al repositorio remoto (hasta que uno aprenda a lidiar con el protocolo SSH, que es más seguro). Con git push se copian los cambios del repositorio local hacia el remoto Con git pull se copian los cambios desde el repositorio remoto hacia el repositorio local. Con git clone se copia un repositorio remoto para crear un repositorio local con un remote automáticamente llamado origin. ¡basta de hacer chistes malos!

No te vayas sin comentar algo del post, de las noticias del día, del clima, que se yo...

Tutorial sobre control de versiones (parte 4) Indice del Tutorial o o o o

Parte 1. Cómo armar un repositorio local Parte 2. Cómo subir el repositorio local al remoto Parte 3. Cómo colaborar en un mismo repositorio remoto Parte 4. Cómo resolver conflictos

Para ver las versiones (en inglés) en las cuales se basa este tutorial, podés visitar la página de Software Carpentry


Cuándo aparecen los conflictos El sistema de control de versiones permite que la gente trabaje en paralelo editando sus programas en código fuente. En realidad, se puede usar para cualquier tipo de archivo con texto plano (me parece fantástico para informes, relatos, blogs como este, etc). Trabajar en paralelo implica que en algún momento dos personas se van a pisar y van a modificar una misma porción de texto. Esto podría pasarle incluso a una sola persona: si trabajamos un mismo fragmento en la computadora de escritorio en casa, en una notebook y también en una PC en el laburo, podríamos haber hecho diferentes cambios en cada copia. El control de versiones nos ayuda a manipular esos conflictos dándonos herramientas para resolver esos cambios superpuestos.

Un conflicto impostado Para ver cómo se resuelven estos conflictos vamos a subir al repositorio emacs que posee eldiegoefe el archivo manifiesto.txt con el siguiente texto: Un fantasma recorre Europa: el fantasma del comunismo. Todas las fuerzas de la vieja Europa se han unido en santa cruzada para acosar a ese fantasma: el Papa y el zar, Metternich y Guizot, los radicales franceses y los polizontes alemanes.

A continuación vamos a hacer que los dos usuarios con los que trabajamos en la parte 3 (eldiegoefe y bioingenierodiegol)

realicen

cambios

de

este

archivo

en

repositorios locales. El usuario bioingenierodiegol agregar una linea a su versión local de manifiesto.txt: Un fantasma recorre Europa: el fantasma del comunismo. Todas las fuerzas de la vieja Europa se han unido en santa cruzada para acosar a ese fantasma: el Papa y el zar, Metternich y Guizot, los radicales franceses y los polizontes alemanes. Marx se la come.

sus


Mientras tanto el usuario eldiegoefe agrega otra oración, un poco menos revisionista: Un fantasma recorre Europa: el fantasma del comunismo. Todas las fuerzas de la vieja Europa se han unido en santa cruzada para acosar a ese fantasma: el Papa y el zar, Metternich y Guizot, los radicales franceses y los polizontes alemanes. Marx es re-grosso, vieja.

Por

cosas

de

la

vida, bioingenierodiegol hace

su

secuencia 1.

git

add

manifiesto.txt, 2. git commit -m “Agregue linea ‘Marx se la come’” y 3. git push origin master de modo que cuando termina de hacerlo, en el repositorio remoto (en Github) el archivo manifiesto.txt habla de la gastronómica vida sexual de Marx.

Experimentando la frustración del militante El respetuoso militante eldiegoefe también modifica su copia local de manifiesto.txt, hace su git add y su git commit pero veamos qué sucede cuando intenta el git push: [eldiegoefe]$ git push origin master To https://github.com/eldiegoefe/emacs.git ! [rejected]

master -> master (fetch first)

error: failed to push some refs to 'https://github.com/eldiegoefe/emacs.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.

El sistema nos da un error, pero también nos dice el motivo por el cual fue rechazado el push: el remoto contiene trabajo que no poseemos localmente. Esto es frecuentemente causado por otro usuario actualizando el repositorio en Github en el tiempo que transcurre entre el momento que hicimos la última sincronización con el repositorio remoto y el tiempo en el que intentamos el git push. En su mensaje, como el sistema


sabe de esta posibilidad, sugiere primero integrar los cambios del remoto con un git pull antes de volver a intentar nuestro git push.

Comenzando a solucionar la cuestiĂłn Es decir que Git detecta que los cambios hechos en una copia se superponen con los hechos en la otra copia, y nos impide pisar o sobreescribir el trabajo. Vamos entonces a hacer el git pull desde Github y fundir esos cambios remotos en nuestra copia local. [eldiegoefe]$ git pull origin master remote: Counting objects: 3, done. remote: Compressing objects: 100% (1/1), done. remote: Total 3 (delta 2), reused 3 (delta 2) Unpacking objects: 100% (3/3), done. From https://github.com/eldiegoefe/emacs * branch 142c683..efe8a9a

master

-> FETCH_HEAD

master

-> origin/master

Auto-merging manifiesto.txt CONFLICT (content): Merge conflict in manifiesto.txt Automatic merge failed; fix conflicts and then commit the result.

Ahora Git nos avisa que hay un conflicto y marca el archivo donde ĂŠste se produce (manifiesto.txt). Veamos ahora el contenido de este archivo con cualquier editor: Un fantasma recorre Europa: el fantasma del comunismo. Todas las <<<<<<< HEAD fuerzas de la vieja Europa se han unido en santa cruzada para acosar a ese fantasma: el Papa y el zar, Metternich y Guizot, los radicales franceses y los polizontes alemanes. Marx es re-grosso, vieja. ======= fuerzas de la vieja Europa se han unido en santa cruzada para


acosar a ese fantasma: el Papa y el zar, Metternich y Guizot, los radicales franceses y los polizontes alemanes. Marx se la come. >>>>>>> efe8a9a434f1a2609a16660f7d78c82fadad7d7c

Vemos que Git ha modificado el archivo local colocando marcas para separar las dos versiones. Veo que ambas versiones no solamente difieren en la oración que habla de Marx, sino también en el contenido de las últimas tres lineas (no empiezan y terminan con las mismas palabras). ¡No es facil engañar a Git! Las marcas son <<<<<<< HEAD, el separador ======= (que divide los cambios conflictivos

en

las

dos

versiones)

y >>>>>>>

efe8a9a434f1a2609a16660f7d78c82fadad7d7c. Lo que está junto alHEAD es el contenido local, mientras lo que está tras el separador (antes del identificador de la revisión que acabamos de bajar) es el contenido agregado remotamente.

Solución en proceso Nos corresponde editar este archivo para remover las marcas y reconciliar los cambios. Podemos hacer lo que nos plazca: mantener los cambios que hicimos en el repositorio local, mantener los cambios hechos en el repositorio remoto, escribir algo nuevo que reemplace a ambos, o eliminar completamente ambos cambios. Hagamos una mezcla: Un fantasma recorre Europa: el fantasma del comunismo. Todas las fuerzas de la vieja Europa se han unido en santa cruzada para acosar a ese fantasma: el Papa y el zar, Metternich y Guizot, los radicales franceses y los polizontes alemanes. Marx es re-grosso, pero Engels es más grosso todavía.

Ahora eldiegoefe pide enterarse de cómo está la situación: [eldiegoefe]$ git status # On branch master # You have unmerged paths.


#

(fix conflicts and run "git commit")

# # Unmerged paths: #

(use "git add <file>..." to mark resolution)

# #

both modified:

manifiesto.txt

# no changes added to commit (use "git add" and/or "git commit -a")

Tenemos que agregar los cambios y comitearlos antes de volver a intentar el push: [eldiegoefe]$ git add manifiesto.txt

[eldiegoefe]$ git status # On branch master # All conflicts fixed but you are still merging. #

(use "git commit" to conclude merge)

# # Changes to be committed: # #

modified:

manifiesto.txt

[eldiegoefe]$ git commit -m "Conflicto arreglado en manifiesto.txt" [master 4f17908] Conflicto arreglado en manifiesto.txt

[eldiegoefe]$ git status # On branch master nothing to commit, working directory clean


Listo el pollo Ahora sí intentamos el push: [eldiegoefe]$ git push origin master Counting objects: 10, done. Delta compression using up to 4 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 665 bytes | 0 bytes/s, done. Total 6 (delta 4), reused 0 (delta 0) To https://github.com/eldiegoefe/emacs.git efe8a9a..4f17908

master -> master

Esta vez pudimos subir todo exitosamente. Ahora vamos a ver qué le pasa al otro usuario bioingenierodiegol cuando quiere actualizar su repo local: [bioingenierodiegol] $ git pull origin master remote: Counting objects: 6, done. remote: Compressing objects: 100% (2/2), done. Unpacking objects: 100% (6/6), done. remote: Total 6 (delta 4), reused 6 (delta 4) From https://github.com/eldiegoefe/emacs * branch efe8a9a..4f17908

master

-> FETCH_HEAD

master

-> origin/master

Updating efe8a9a..4f17908 Fast-forward manifiesto.txt | 3 ++1 file changed, 2 insertions(+), 1 deletion(-)

Sin problemas se ha cerrado el círculo, ambos usuarios tienen el mismo contenido en el archivo que había generado el conflicto:


Un fantasma recorre Europa: el fantasma del comunismo. Todas las fuerzas de la vieja Europa se han unido en santa cruzada para acosar a ese fantasma: el Papa y el zar, Metternich y Guizot, los radicales franceses y los polizontes alemanes. Marx es re-grosso, pero Engels es más grosso todavía.

El usuario bioingenierodiegol no tuvo necesidad de hacer la fusión (merge) porque Git sabe que algún otro ya lo hizo. Los usuarios tienden a dividir sus programas y textos en múltiples archivos (en vez de meter todo en un mismo archivo enorme) porque esto facilita lo que acabamos de ver: habilidad del sistema de control de versiones para fusionar cambios conflictivos. Hay también otro beneficio: siempre que hay conflictos repetidos en un archivo en particular, el sistema de control de versiones está esencialmente tratando de decirle a sus usuarios que deberían clarificar quién es el responsable de cada cosa, o encontrar un modo de dividir el trabajo de modo diferente.

Claves Los conflictos ocurren cuando dos o más personas cambian el mismo archivo al mismo tiempo. El sistema de control de versiones no permite que la gente sobreescriba ciegamente los cambios realizados por otras personas. En cambio, resalta los conflictos para que los puedan resolver. No te vayas sin comentar algo del post, de las noticias del día, del clima, que se yo...


Links https://eldiegoefe.github.io/tutorial-sobre-control-de-versiones-parte-1.html https://eldiegoefe.github.io/tutorial-sobre-control-de-versiones-parte-2.html https://eldiegoefe.github.io/tutorial-sobre-control-de-versiones-parte-3.html https://eldiegoefe.github.io/tutorial-sobre-control-de-versiones-parte-4.html

Angular + Spring boot

Build a Basic CRUD App with Angular 5.0 and Spring Boot 2.0 https://developer.okta.com/blog/2017/12/04/basic-crud-angular-and-spring-boot

Technology moves fast these days. It can be challenging to keep up with the latest trends as well as new releases of your favorite projects. I’m here to help! Spring Boot and Angular are two of my favorite projects, so I figured I’d write y’all a guide to show you how to build and secure a basic app using their latest and greatest releases. In Spring Boot, the most significant change in 2.0 is its new web framework: Spring WebFlux. In Angular 5.0, we get a new HttpClient on the table. This class replaces Http, and is a bit easier to use, with less boilerplate code. Today, I’m not going to explore Spring WebFlux, because we still have some work to do before we can support in with the Okta Spring Boot Starter.


The good news is our Angular SDK works well with Angular 5, so I’ll be showing how to use it in this blog post. Speaking of Angular, did you know that Angular has one of the most dramatic increases in questions on Stack Overflow? You might think this means a lot of people have issues with Angular. I like to think that there’s a lot of people using it, and developers often have questions when using a new technology. It’s a definite sign of a healthy community. You rarely see a lot of questions on Stack Overflow for a dying technology.

This article describes how to build a simple CRUD application that displays a list of cool cars. It’ll allow you to edit the list, and it’ll show an animated gif from GIPHY that matches the car’s name. You’ll also learn how to secure your application using Okta’s Spring Boot starter and Angular SDK. You will need Java 8 and Node.js 8 installed to complete this tutorial.

Build an API with Spring Boot 2.0 To get started with Spring Boot 2.0, head on over to start.spring.io and create a new project that uses Java, Spring Boot version 2.0.1, and options to create a


simple API: JPA, H2, Rest Repositories, Lombok, and Web. In this example, I’ve added Actuator as well, since it’s a very cool feature of Spring Boot.

Create a directory to hold your server and client applications. I called mine oktaspring-boot-2-angular-5-example, but you can call yours whatever you like. If you’d rather just see the app running than write code, you can see the example on GitHub, or clone and run locally using the commands below. git clone https://github.com/oktadeveloper/okta-spring-boot-2-angular5-example.git

cd okta-spring-boot-2-angular-5-example/client && npm install && ng serve & cd ../server && ./mvnw spring-boot:run After downloading demo.zip from start.spring.io, expand it and copy the demo directory to your app-holder directory. Rename demo to server. Open the project in your favorite IDE and create a Car.java file in the src/main/java/com/okta/developer/demo directory. You can use Lombok’s annotations to reduce boilerplate code. package com.okta.developer.demo;


import lombok.*;

import javax.persistence.Id; import javax.persistence.GeneratedValue; import javax.persistence.Entity;

@Entity @Getter @Setter @NoArgsConstructor @ToString @EqualsAndHashCode public class Car { @Id @GeneratedValue private Long id; private @NonNull String name; } Create a CarRepository class to perform CRUD (create, read, update, and delete) on the Car entity. package com.okta.developer.demo;

import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.rest.core.annotation.RepositoryRestR esource;


@RepositoryRestResource interface CarRepository extends JpaRepository<Car, Long> { } Add an ApplicationRunner bean to the DemoApplication class (in src/main/java/com/okta/developer/demo/DemoApplication.java) and use it to add some default data to the database. package com.okta.developer.demo;

import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import java.util.stream.Stream;

@SpringBootApplication public class DemoApplication {

public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }

@Bean


ApplicationRunner init(CarRepository repository) { return args -> { Stream.of("Ferrari", "Jaguar", "Porsche", "Lamborghini", "Bugatti", "AMC Gremlin", "Triumph Stag", "Ford Pinto", "Yugo GV").forEach(name -> { Car car = new Car(); car.setName(name); repository.save(car); }); repository.findAll().forEach(System.out::println); }; } } If you start your app (using ./mvnw spring-boot:run) after adding this code, you’ll see the list of cars displayed in your console on startup. Car(id=1, name=Ferrari) Car(id=2, name=Jaguar) Car(id=3, name=Porsche) Car(id=4, name=Lamborghini) Car(id=5, name=Bugatti) Car(id=6, name=AMC Gremlin) Car(id=7, name=Triumph Stag) Car(id=8, name=Ford Pinto)


Car(id=9, name=Yugo GV) Add a CoolCarController class (in src/main/java/com/okta/developer/demo/CoolCarController.jav a) that returns a list of cool cars to display in the Angular client. package com.okta.developer.demo;

import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Collection; import java.util.stream.Collectors;

@RestController class CoolCarController { private CarRepository repository;

public CoolCarController(CarRepository repository) { this.repository = repository; }

@GetMapping("/cool-cars") public Collection<Car> coolCars() { return repository.findAll().stream() .filter(this::isCool)


.collect(Collectors.toList()); }

private boolean isCool(Car car) { return !car.getName().equals("AMC Gremlin") && !car.getName().equals("Triumph Stag") && !car.getName().equals("Ford Pinto") && !car.getName().equals("Yugo GV"); } } If you restart your server app and hit localhost:8080/cool-cars with your browser, or a command-line client, you should see the filtered list of cars. http localhost:8080/cool-cars HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 Date: Mon, 05 Mar 2018 12:31:32 GMT Transfer-Encoding: chunked [ { "id": 1, "name": "Ferrari" }, {


"id": 2, "name": "Jaguar" }, { "id": 3, "name": "Porsche" }, { "id": 4, "name": "Lamborghini" }, { "id": 5, "name": "Bugatti" } ]

Create a Client with Angular CLI Angular CLI is a command-line utility that can generate an Angular project for you. Not only can it create new projects, but it can also generate code. It’s a convenient tool because it also offers commands that will build and optimize your project for production. It uses webpack under the covers for building. If you want to learn more about webpack, I recommend webpack.academy. You can learn the basics of Angular CLI at https://cli.angular.io.


Install the latest version of Angular CLI, which is version 1.7.4. npm install -g @angular/cli@1.7.4 Create a new project in the umbrella directory you created. Again, mine is named okta-spring-boot-2-angular-5-example. ng new client After the client is created, navigate into its directory and install Angular Material. cd client npm install --save-exact @angular/material@5.2.4 @angular/cdk@5.2.4 You’ll use Angular Material’s components to make the UI look better, especially on mobile phones. If you’d like to learn more about Angular Material, see https://material.angular.io. It has extensive documentation on its various components and how to use them.


Build a Car List Page Use Angular CLI to generate a car service that can talk to the Cool Cars API. ng g s car Move the generated files into the client/src/app/shared/car directory. mkdir -p src/app/shared/car mv src/app/car.service.* src/app/shared/car/. Update the code in car.service.ts to fetch the list of cars from the server. import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http';


import { Observable } from 'rxjs/Observable';

@Injectable() export class CarService {

constructor(private http: HttpClient) { }

getAll(): Observable<any> { return this.http.get('//localhost:8080/cool-cars'); } } TIP: If you’re using using Angular 6+, you can make this code work by installing rxjs-compat with npm i rxjs-compat. Add this service as a provider in src/app/app.module.ts. While you’re in there, import HttpClientModule too. import { CarService } from './shared/car/car.service'; import { HttpClientModule } from '@angular/common/http';

@NgModule({ declarations: [ AppComponent ],


imports: [ BrowserModule, HttpClientModule ], providers: [CarService], bootstrap: [AppComponent] }) Generate a car-list component to display the list of cars. ng g c car-list Update client/src/app/car-list/car-list.component.ts to use the CarService to fetch the list and set the values in a local cars variable. import { CarService } from '../shared/car/car.service';

export class CarListComponent implements OnInit { cars: Array<any>;

constructor(private carService: CarService) { }

ngOnInit() { this.carService.getAll().subscribe(data => { this.cars = data; }); }


} Update client/src/app/car-list/car-list.component.html to show the list of cars. <h2>Car List</h2>

<div *ngFor="let car of cars"> {{car.name}} </div> Update client/src/app/app.component.html to have the app-carlist element. <div style="text-align:center"> <h1>Welcome to {{title}}!</h1> </div>

<app-car-list></app-car-list> Start the client application using ng serve. Open your favorite browser to http://localhost:4200. You won’t see the car list just yet, and if you open your developer console, you’ll see why.


This error happens because you haven’t enabled CORS (Cross-Origin Resource Sharing) on the server.

Enable CORS on the Server To enable CORS on the server, add a @CrossOrigin annotation to the CoolCarController (in server/src/main/java/com/okta/develope r/demo/CoolCarController.java). import org.springframework.web.bind.annotation.CrossOrigin; ... @GetMapping("/cool-cars") @CrossOrigin(origins = "http://localhost:4200") public Collection<Car> coolCars() { return repository.findAll().stream() .filter(this::isCool)


.collect(Collectors.toList()); } Also, add it to CarRepository so you can communicate with its endpoints when adding/deleting/editing. @RepositoryRestResource @CrossOrigin(origins = "http://localhost:4200") interface CarRepository extends JpaRepository<Car, Long> { } Restart the server, refresh the client, and you should see the list of cars in your browser.

Add Angular Material You’ve already installed Angular Material, to use its components, you simply need to import them. Open client/src/app/app.module.ts and add imports for animations, and Material’s toolbar, buttons, inputs, lists, and card layout. import { MatButtonModule, MatCardModule, MatInputModule, MatListModule, MatToolbarModule } from '@angular/material'; import { BrowserAnimationsModule } from '@angular/platformbrowser/animations';

@NgModule({ ... imports: [ BrowserModule, HttpClientModule,


BrowserAnimationsModule, MatButtonModule, MatCardModule, MatInputModule, MatListModule, MatToolbarModule ], ... }) Update client/src/app/app.component.html to use the toolbar component. <mat-toolbar color="primary"> <span>Welcome to {{title}}!</span> </mat-toolbar>

<app-car-list></app-car-list> Update client/src/app/car-list/car-list.component.html to use the card layout and list component. <mat-card> <mat-card-header>Car List</mat-card-header> <mat-card-content> <mat-list> <mat-list-item *ngFor="let car of cars"> <img mat-list-avatar src="{{car.giphyUrl}}" alt="{{car.name}}">


<h3 mat-line>{{car.name}}</h3> </mat-list-item> </mat-list> </mat-card-content> </mat-card> Modify client/src/styles.css to specify the theme and icons. @import "~@angular/material/prebuilt-themes/pinkbluegrey.css"; @import 'https://fonts.googleapis.com/icon?family=Material+Icons';

body { margin: 0; font-family: Roboto, sans-serif; } If you run your client with ng serve and navigate to http://localhost:4200, you’ll see the list of cars, but no images associated with them.


Add Animated GIFs with Giphy To add a giphyUrl property to cars, create client/src/app/shared/giphy/giphy.service.ts and populate it with the code below. import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import 'rxjs/add/operator/map';

@Injectable() export class GiphyService {


// Public beta key: https://github.com/Giphy/GiphyAPI#public-betakey

giphyApi = '//api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=1&q=';

constructor(public http: HttpClient) { }

get(searchTerm) { const apiLink = this.giphyApi + searchTerm; return this.http.get(apiLink).map((response: any) => { if (response.data.length > 0) { return response.data[0].images.original.url; } else { return 'https://media.giphy.com/media/YaOxRsmrv9IeA/giphy.gif'; // dancing cat for 404 } }); } } Add GiphyService as a provider in client/src/app/app.module.ts. import { GiphyService } from './shared/giphy/giphy.service';

@NgModule({


... providers: [CarService, GiphyService], bootstrap: [AppComponent] }) Update the code in client/src/app/car-list/car-list.component.ts to set the giphyUrl property on each car. import { GiphyService } from '../shared/giphy/giphy.service';

export class CarListComponent implements OnInit { cars: Array<any>;

constructor(private carService: CarService, private giphyService: GiphyService) { }

ngOnInit() { this.carService.getAll().subscribe(data => { this.cars = data; for (const car of this.cars) { this.giphyService.get(car.name).subscribe(url => car.giphyUrl = url); } }); } }


Now your browser should show you the list of car names, along with an avatar image beside them.

Add an Edit Feature Having a list of car names and images is cool, but it’s a lot more fun when you can interact with it! To add an edit feature, start by generating a car-edit component. ng g c car-edit Update client/src/app/shared/car/car.service.ts to have methods for adding, removing, and updating cars. These methods talk to the endpoints provided by the CarRepository and the @RepositoryRestResource annotation. import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs/Observable';


@Injectable() export class CarService { public API = '//localhost:8080'; public CAR_API = this.API + '/cars';

constructor(private http: HttpClient) { }

getAll(): Observable<any> { return this.http.get(this.API + '/cool-cars'); }

get(id: string) { return this.http.get(this.CAR_API + '/' + id); }

save(car: any): Observable<any> { let result: Observable<Object>; if (car['href']) { result = this.http.put(car.href, car); } else { result = this.http.post(this.CAR_API, car);


} return result; }

remove(href: string) { return this.http.delete(href); } } In client/src/app/car-list/car-list.component.html, add a link to the edit component. Also, add a button at the bottom to add a new car. <mat-card> <mat-card-header>Car List</mat-card-header> <mat-card-content> <mat-list> <mat-list-item *ngFor="let car of cars"> <img mat-list-avatar src="{{car.giphyUrl}}" alt="{{car.name}}"> <h3 mat-line> <a mat-button [routerLink]="['/car-edit', car.id]">{{car.name}}</a> </h3> </mat-list-item> </mat-list> </mat-card-content>


<button mat-fab color="primary" [routerLink]="['/caradd']">Add</button> </mat-card> In client/src/app/app.module.ts, add routes and import the FormsModule. import { FormsModule } from '@angular/forms'; import { RouterModule, Routes } from '@angular/router';

const appRoutes: Routes = [ { path: '', redirectTo: '/car-list', pathMatch: 'full' }, { path: 'car-list', component: CarListComponent }, { path: 'car-add', component: CarEditComponent }, { path: 'car-edit/:id', component: CarEditComponent } ];


@NgModule({ ... imports: [ ... FormsModule, RouterModule.forRoot(appRoutes) ], ... }) Modify client/src/app/car-edit/car-edit.component.ts to fetch a car’s information from the id passed on the URL, and to add methods for saving and deleting. import { Component, OnDestroy, OnInit } from '@angular/core'; import { Subscription } from 'rxjs/Subscription'; import { ActivatedRoute, Router } from '@angular/router'; import { CarService } from '../shared/car/car.service'; import { GiphyService } from '../shared/giphy/giphy.service'; import { NgForm } from '@angular/forms';

@Component({ selector: 'app-car-edit', templateUrl: './car-edit.component.html', styleUrls: ['./car-edit.component.css']


}) export class CarEditComponent implements OnInit, OnDestroy { car: any = {};

sub: Subscription;

constructor(private route: ActivatedRoute, private router: Router, private carService: CarService, private giphyService: GiphyService) { }

ngOnInit() { this.sub = this.route.params.subscribe(params => { const id = params['id']; if (id) { this.carService.get(id).subscribe((car: any) => { if (car) { this.car = car; this.car.href = car._links.self.href; this.giphyService.get(car.name).subscribe(url => car.giphyUrl = url); } else {


console.log(`Car with id '${id}' not found, returning to list`); this.gotoList(); } }); } }); }

ngOnDestroy() { this.sub.unsubscribe(); }

gotoList() { this.router.navigate(['/car-list']); }

save(form: NgForm) { this.carService.save(form).subscribe(result => { this.gotoList(); }, error => console.error(error)); }


remove(href) { this.carService.remove(href).subscribe(result => { this.gotoList(); }, error => console.error(error)); } } Update the HTML in client/src/app/car-edit/caredit.component.html to have a form with the car’s name, as well as to display the image from Giphy. <mat-card> <form #carForm="ngForm" (ngSubmit)="save(carForm.value)"> <mat-card-header> <mat-card-title><h2>{{car.name ? 'Edit' : 'Add'}} Car</h2></mat-card-title> </mat-card-header> <mat-card-content> <input type="hidden" name="href" [(ngModel)]="car.href"> <mat-form-field> <input matInput placeholder="Car Name" [(ngModel)]="car.name" required name="name" #name> </mat-form-field> </mat-card-content> <mat-card-actions>


<button mat-raised-button color="primary" type="submit" [disabled]="!carForm.form.valid">Save</button> <button mat-raised-button color="secondary" (click)="remove(car.href)" *ngIf="car.href" type="button">Delete</button> <a mat-button routerLink="/car-list">Cancel</a> </mat-card-actions> <mat-card-footer> <div class="giphy"> <img src="{{car.giphyUrl}}" alt="{{car.name}}"> </div> </mat-card-footer> </form> </mat-card> Put a little padding around the image by adding the following CSS to client/src/app/car-edit/car-edit.component.css. .giphy { margin: 10px; } Modify client/src/app/app.component.html and replace <app-carlist></app-car-list> with <router-outlet></router-outlet>. This change is necessary or routing between components won’t work. <mat-toolbar color="primary"> <span>Welcome to {{title}}!</span> </mat-toolbar>


<router-outlet></router-outlet> After you make all these changes, you should be able to add, edit, or delete any cars. Below is a screenshot that shows the list with the add button.

The following screenshot shows what it looks like to edit a car that you’ve added.



Add Authentication with Okta Add authentication with Okta is a nifty feature you can add to this application. Knowing who the person is can come in handy if you want to add auditing, or personalize your application (with a rating feature for example).

Spring Security + OAuth 2.0 On the server side, you can lock things down with Spring Security and its OAuth 2.0 support. Open server/pom.xml and add the following dependencies. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2autoconfigure</artifactId> <version>2.0.0.RELEASE</version> </dependency> Now you need to configure the server to use Okta for authentication. You’ll need to create an OIDC app in Okta for that.


Create an OIDC App in Okta Log in to your Okta Developer account (or sign up if you don’t have an account) and navigate to Applications > Add Application. Click Single-Page App, click Next, and give the app a name you’ll remember. Change all instances of localhost:8080 to localhost:4200 and click Done. Create server/src/main/resources/application.yml and copy the client ID into it. While you’re in there, fill in the rest of the necessary values to match your Okta domain. security: oauth2: client: access-token-uri: https://{yourOktaDomain}/oauth2/default/v1/token user-authorization-uri: https://{yourOktaDomain}/oauth2/default/v1/authorize

client-id: {clientId} scope: openid profile email resource: user-info-uri: https://{yourOktaDomain}/oauth2/default/v1/userinfo

token-info-uri: https://{yourOktaDomain}/oauth2/default/v1/introspect prefer-token-info: false Update server/src/main/java/com/okta/developer/demo/DemoApplic ation.java to enable it as a resource server. import org.springframework.security.oauth2.config.annotation.web.con figuration.EnableResourceServer;


@EnableResourceServer @SpringBootApplication After making these changes, you should be able to restart your app and see access denied when you try to navigate to http://localhost:8080.

It’s nice that your server is locked down, but now you need to configure your client to talk to it. This is where Okta’s Angular support comes in handy.

Okta’s Angular Support The Okta Angular SDK is a wrapper around Okta Auth JS, which builds on top of OIDC. More information about Okta’s Angular library can be found on npmjs.com.


To install it, run the following command in the client directory: npm install @okta/okta-angular@1.0.0 In client/src/app/app.module.ts, add a config variable with the settings for your OIDC app. const config = { issuer: 'https://{yourOktaDomain}/oauth2/default', redirectUri: 'http://localhost:4200/implicit/callback', clientId: '{clientId}' }; In this same file, you’ll also need to add a new route for the redirectUri that points to the OktaCallbackComponent.


import { OktaCallbackComponent, OktaAuthModule } from '@okta/okta-angular';

const appRoutes: Routes = [ ... { path: 'implicit/callback', component: OktaCallbackComponent } ]; Next, initialize and import the OktaAuthModule. @NgModule({ ... imports: [ ... OktaAuthModule.initAuth(config) ], ... }) These are the three steps you need to set up an Angular app to use Okta. To make it easy to add a bearer token to HTTP requests, you can use an HttpInterceptor. Create client/src/app/shared/okta/auth.interceptor.ts and add the following code to it. import { Injectable } from '@angular/core';


import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/fromPromise'; import { OktaAuthService } from '@okta/okta-angular';

@Injectable() export class AuthInterceptor implements HttpInterceptor {

constructor(private oktaAuth: OktaAuthService) { }

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return Observable.fromPromise(this.handleAccess(request, next)); }

private async handleAccess(request: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> { // Only add to known domains since we don't want to send our tokens to just anyone. // Also, Giphy's API fails when the request includes a token. if (request.urlWithParams.indexOf('localhost') > -1) {


const accessToken = await this.oktaAuth.getAccessToken(); request = request.clone({ setHeaders: { Authorization: 'Bearer ' + accessToken } }); } return next.handle(request).toPromise(); } } To register this interceptor, add it as a provider in client/src/app/app.module.ts. import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthInterceptor } from './shared/okta/auth.interceptor';

@NgModule({ ... providers: [CarService, GiphyService, {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true}], ... })


Modify client/src/app/app.component.html to have login and logout buttons. <mat-toolbar color="primary"> <span>Welcome to {{title}}!</span> <span class="toolbar-spacer"></span> <button mat-raised-button color="accent" *ngIf="isAuthenticated" (click)="oktaAuth.logout()">Logout </button> </mat-toolbar>

<mat-card *ngIf="!isAuthenticated"> <mat-card-content> <button mat-raised-button color="accent" (click)="oktaAuth.loginRedirect()">Login </button> </mat-card-content> </mat-card>

<router-outlet></router-outlet> You might notice there’s a span with a toolbar-spacer class. To make that work as expected, update client/src/app/app.component.css to have the following class. .toolbar-spacer { flex: 1 1 auto;


} There’s also a reference to isAuthenticated for checking authenticated status. To make this work, add it as a dependency to the constructor in client/src/app/app.component.ts and add an initializer method that sets the variable. import { Component, OnInit } from '@angular/core'; import { OktaAuthService } from '@okta/okta-angular';

@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { title = 'app'; isAuthenticated: boolean;

constructor(private oktaAuth: OktaAuthService) { }

async ngOnInit() { this.isAuthenticated = await this.oktaAuth.isAuthenticated(); // Subscribe to authentication state changes this.oktaAuth.$authenticationState.subscribe(


(isAuthenticated: boolean) isAuthenticated

=> this.isAuthenticated =

); } } Now if you restart your client, you should see a login button.

Notice that this shows elements from the car-list component. To fix this, you can create a home component and make it the default route. ng g c home Modify client/src/app/app.module.ts to update the routes. const appRoutes: Routes = [ {path: '', redirectTo: '/home', pathMatch: 'full'}, { path: 'home', component: HomeComponent },


... } Move the HTML for the Login button from app.component.html to client/src/app/home/home.component.ht ml. <mat-card> <mat-card-content> <button mat-raised-button color="accent" *ngIf="!isAuthenticated" (click)="oktaAuth.loginRedirect()">Login </button> <button mat-raised-button color="accent" *ngIf="isAuthenticated" [routerLink]="['/car-list']">Car List </button> </mat-card-content> </mat-card> Add oktaAuth as a dependency in client/src/app/home/home.component.ts and set it up to initialize/change the isAuthenticated variable. import { OktaAuthService } from '@okta/okta-angular';

export class HomeComponent implements OnInit { isAuthenticated: boolean;

constructor(private oktaAuth: OktaAuthService) {


}

async ngOnInit() { this.isAuthenticated = await this.oktaAuth.isAuthenticated(); // Subscribe to authentication state changes this.oktaAuth.$authenticationState.subscribe( (isAuthenticated: boolean) isAuthenticated

=> this.isAuthenticated =

); } } Update client/src/app/app.component.html, so the Logout button redirects back to home when it’s clicked. <mat-toolbar color="primary"> <span>Welcome to {{title}}!</span> <span class="toolbar-spacer"></span> <button mat-raised-button color="accent" *ngIf="isAuthenticated" (click)="oktaAuth.logout()" [routerLink]="['/home']">Logout </button> </mat-toolbar>

<router-outlet></router-outlet>


Now you should be able to open your browser to http://localhost:4200 and click on the Login button. If you’ve configured everything correctly, you’ll be redirected to Okta to log in.

Enter the credentials you used to sign up for an account, and you should be redirected back to your app. However, your list of cars won’t load because of a CORS error. This happens because Spring’s @CrossOrigin doesn’t work well with Spring Security. To fix this, add a bean to DemoApplication.java that handles CORS. import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.core.Ordered; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import java.util.Collections;


...

@Bean @SuppressWarnings("unchecked") public FilterRegistrationBean simpleCorsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.setAllowedOrigins( Collections.singletonList("http://localhost:4200")); config.setAllowedMethods(Collections.singletonList("*")); config.setAllowedHeaders(Collections.singletonList("*")); source.registerCorsConfiguration("/**", config); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); bean.setOrder(Ordered.HIGHEST_PRECEDENCE); return bean; } Restart your server and celebrate when it all works! đ&#x;Ž‰


You can see the full source code for the application developed in this tutorial on GitHub at https://github.com/oktadeveloper/okta-spring-boot-2-angular-5-example.

Spring Boot 2 + Angular 7 CRUD Example Tutorial Link: https://www.javaguides.net/2019/02/spring-boot-2-angular-7-crud-exampletutorial.html?fbclid=IwAR3dtxbT_pKqVJL8g_XlBH55JxdkH2tfa66mWmKvx1Ls_TcU6J87bvHAi5M

In this tutorial, we will learn how to develop a CRUD (Create, Read, Update, Delete) Web Application using Angular 7 as a front-end and Spring boot 2 restful API as a backend. If you are looking for Angular 6 with spring boot 2 integration example then check out Spring Boot + Angular 6 CRUD Example article.


You can download the source code of this tutorial from my GitHub repository at end of this tutorial.

What we will build? Basically, we will create two projects: 1. springboot2-jpa-crud-example: This project is used to develop CRUD RESTFul APIs for a simple Employee Management System using Spring Boot 2, JPA and MySQL as a database. 2. angular7-springboot-client: This project is used to develop single page application using Angular 7 as front-end technology. This Angular 7 application consumes CRUD Restful APIs developed and exposed by a springboot2-jpa-crudexample project.


Let me list out tools and technologies used to develop these two applications.

Tools and technologies used

Server-side technologies 

Spring Boot - 2.0.4.RELEASE

JDK - 1.8 or later

Spring Framework - 5.0.8 RELEASE

Hibernate - 5.2.17.Final

Spring Data JPA - 2+

Front end technologies 

Angular 7.2

Bootstrap 4

npm- 6.4.1

JQuery

Tools 

Maven - 3.2+

IDE - Eclipse or Spring Tool Suite (STS)

Visual Studio 2017

Angular CLI

Spring Boot CRUD Rest APIs Let's first we will build a CRUD RESTFul APIs for a Simple Employee Management System using Spring Boot 2 JPA and MySQL. Later we will consume these Rest APIs using Angular 7 client application. Following are five REST APIs (Controller handler methods) are created for Employee resource.


1. Creating and Importing a Project There are many ways to create a Spring Boot application. The simplest way is to use Spring Initializrat http://start.spring.io/, which is an online Spring Boot application generator.


Look at the above diagram, we have specified the following details:          

Generate: Maven Project Java Version: 1.8 (Default) Spring Boot:2.0.4 Group: net.guides.springboot2 Artifact: springboot2-jpa-crud-example Name: springboot2-jpa-crud-example Description: Rest API for a Simple Employee Management Application Package Name : net.guides.springboot2.springboot2jpacrudexample Packaging: jar (This is the default value) Dependencies: Web, JPA, MySQL, DevTools


Once, all the details are entered, click on Generate Project button will generate a spring boot project and downloads it. Next, Unzip the downloaded zip file and import it into your favorite IDE.

2. Packaging Structure Following is the packing structure of our Employee Management System -

3. The pom.xml File

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"


xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.guides.springboot2</groupId> <artifactId>springboot2-jpa-crud-example</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springboot2-jpa-crud-example</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins>


<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

4. Configuring MySQL Database Configure application.properties to connect to your MySQL database. Let's open an application.properties file and add the following database configuration to it. Also, note that we have added MySQL dependency to pom.xml so spring boot will autoconfigure all database related beans and configurations internally. spring.datasource.url = jdbc:mysql://localhost:3306/users_database?useSSL=false spring.datasource.username = root spring.datasource.password = root

## Hibernate Properties # The SQL dialect makes Hibernate generate better SQL for the chosen database spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect # Hibernate ddl auto (create, create-drop, validate, update) spring.jpa.hibernate.ddl-auto = update

Change the above configuration such as JDBC URL, username and password as per your environment.

5. Create JPA Entity - Employee.java

package net.guides.springboot2.springboot2jpacrudexample.model; import import import import import import

javax.persistence.Column; javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id; javax.persistence.Table;

@Entity @Table(name = "employees") public class Employee { private long id;


private String firstName; private String lastName; private String emailId; public Employee() { } public Employee(String firstName, String lastName, String emailId) { this.firstName = firstName; this.lastName = lastName; this.emailId = emailId; } @Id @GeneratedValue(strategy = GenerationType.AUTO) public long getId() { return id; } public void setId(long id) { this.id = id; } @Column(name = "first_name", nullable = false) public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @Column(name = "last_name", nullable = false) public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Column(name = "email_address", nullable = false) public String getEmailId() { return emailId; } public void setEmailId(String emailId) { this.emailId = emailId; } @Override public String toString() { return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", emailId=" + emailId + "]"; } }


6. Create Spring Data Repository - EmployeeRepository.java

package net.guides.springboot2.springboot2jpacrudexample.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import net.guides.springboot2.springboot2jpacrudexample.model.Employee; @Repository public interface EmployeeRepository extends JpaRepository<Employee, Long>{ }

7. Create Spring Rest Controller - EmployeeController.java

package net.guides.springboot2.springboot2jpacrudexample.controller; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.validation.Valid; import import import import import import import import import import

org.springframework.beans.factory.annotation.Autowired; org.springframework.http.ResponseEntity; org.springframework.web.bind.annotation.DeleteMapping; org.springframework.web.bind.annotation.GetMapping; org.springframework.web.bind.annotation.PathVariable; org.springframework.web.bind.annotation.PostMapping; org.springframework.web.bind.annotation.PutMapping; org.springframework.web.bind.annotation.RequestBody; org.springframework.web.bind.annotation.RequestMapping; org.springframework.web.bind.annotation.RestController;

import net.guides.springboot2.springboot2jpacrudexample.exception.ResourceNotFou ndException; import net.guides.springboot2.springboot2jpacrudexample.model.Employee; import net.guides.springboot2.springboot2jpacrudexample.repository.EmployeeRepos itory; @RestController @RequestMapping("/api/v1") public class EmployeeController { @Autowired private EmployeeRepository employeeRepository; @GetMapping("/employees")


public List<Employee> getAllEmployees() { return employeeRepository.findAll(); } @GetMapping("/employees/{id}") public ResponseEntity<Employee> getEmployeeById(@PathVariable(value = "id") Long employeeId) throws ResourceNotFoundException { Employee employee = employeeRepository.findById(employeeId) .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId)); return ResponseEntity.ok().body(employee); } @PostMapping("/employees") public Employee createEmployee(@Valid @RequestBody Employee employee) { return employeeRepository.save(employee); } @PutMapping("/employees/{id}") public ResponseEntity<Employee> updateEmployee(@PathVariable(value = "id") Long employeeId, @Valid @RequestBody Employee employeeDetails) throws ResourceNotFoundException { Employee employee = employeeRepository.findById(employeeId) .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId)); employee.setEmailId(employeeDetails.getEmailId()); employee.setLastName(employeeDetails.getLastName()); employee.setFirstName(employeeDetails.getFirstName()); final Employee updatedEmployee = employeeRepository.save(employee); return ResponseEntity.ok(updatedEmployee); } @DeleteMapping("/employees/{id}") public Map<String, Boolean> deleteEmployee(@PathVariable(value = "id") Long employeeId) throws ResourceNotFoundException { Employee employee = employeeRepository.findById(employeeId) .orElseThrow(() -> new ResourceNotFoundException("Employee not found for this id :: " + employeeId)); employeeRepository.delete(employee); Map<String, Boolean> response = new HashMap<>(); response.put("deleted", Boolean.TRUE); return response; } }


8. Running Application This spring boot application has an entry point Java class called SpringBootCrudRestApplication.java with the public static void main(String[] args) method, which you can run to start the application. import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

The main() method uses Spring Boot’s SpringApplication.run() method to launch an application. Or you can start spring boot application via command line using mvn spring-boot:run command. This completes the development of Spring boot CRUD Rest APIs. Now we will develop client application using Angular 7.

Angular 7 Client Application Let's develop a step by step CRUD (Create, Read, Update, Delete) Web Application using Angular 7 which consume above CRUD rest APIs. Good to know Angular 7 release notes and new features at Version 7 of Angular — CLI Prompts, Virtual Scroll, Drag and Drop and more. I assume that you have installed Node.js. Now, we need to check the Node.js and NPM versions. Open the terminal or Node command line then type this commands.

node -v v8.12.0 npm -v 6.4.1

That's the Node.js and NPM version that we are using. Now, you can go to the main steps.


Install or Update Angular 7 CLI and Create Application To install or update Angular 7 CLI, type this command in the Terminal or Node Command Line.

npm install -g @angular/cli

Now, you have the latest version of Angular CLI.

ng --version Angular CLI: 7.0.1 Node: 8.12.0 OS: darwin x64 Angular: ... Package Version -----------------------------------------------------@angular-devkit/architect 0.10.1 @angular-devkit/core 7.0.1 @angular-devkit/schematics 7.0.1 @schematics/angular 7.0.1 @schematics/update 0.10.1 rxjs 6.3.3 typescript 3.1.3

Next, create a new Angular 7 Web Application using this Angular CLI command. The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications. If you are new to Angular CLI then check out official documentation at https://cli.angular.io.

Create Angular 7 client application using Angular CLI Let's use below command to generate an Angular 7 Client application. We name this project as "angular7-springboot-client". ng new angular7-springboot-client


Components, Services, and Modules Let's list out what are components, service, and modules we are going to create in this application. We will use Angular CLI to generate components, services because Angular CLI follows best practices and saves much of time. 1. Components 

create-employee

employee-list

employee-details


2. Services employee.service.ts - Service for Http Client methods

3. Modules 

FormsModule

HttpClientModule

AppRoutingModule.

4. Employee Class (Typescript class) employee.ts: class Employee (id, firstName, lastName, emailId)

In this next step, we will generate these components, classes, and services using Angular CLI.

Create Service & Components Let's auto-generate service and components using Angular CLI. Change your project directory to angular7-springboot-client\src\app and run the following commands: – – –

ng ng ng ng

g g g g

s c c c

employee create-employee employee-details employee-list

Here is complete command and output for your reference: C:\angular7\angular7-springboot-client\src\app>ng g s employee CREATE src/app/employee.service.spec.ts (343 bytes) CREATE src/app/employee.service.ts (137 bytes) C:\angular7\angular7-springboot-client\src\app>ng g c create-employee CREATE src/app/create-employee/create-employee.component.html (34 bytes) CREATE src/app/create-employee/create-employee.component.spec.ts (685 bytes) CREATE src/app/create-employee/create-employee.component.ts (304 bytes) CREATE src/app/create-employee/create-employee.component.css (0 bytes) UPDATE src/app/app.module.ts (509 bytes) C:\angular7\angular7-springboot-client\src\app>ng g c employee-details CREATE src/app/employee-details/employee-details.component.html (35 bytes) CREATE src/app/employee-details/employee-details.component.spec.ts (692 bytes) CREATE src/app/employee-details/employee-details.component.ts (308 bytes)


CREATE src/app/employee-details/employee-details.component.css (0 bytes) UPDATE src/app/app.module.ts (629 bytes) C:\angular7\angular7-springboot-client\src\app>ng g c employee-list CREATE src/app/employee-list/employee-list.component.html (32 bytes) CREATE src/app/employee-list/employee-list.component.spec.ts (671 bytes) CREATE src/app/employee-list/employee-list.component.ts (296 bytes) CREATE src/app/employee-list/employee-list.component.css (0 bytes) UPDATE src/app/app.module.ts (737 bytes)

We will use Bootstrap 4 for styling our application so let's integrate bootstrap 4 with Angular 7.

Integrate Bootstrap with Angular Use NPM to download Bootstrap & JQuery. Bootstrap and jQuery will be installed into the node_modules folder. npm install bootstrap jquery --save

Configure installed Bootstrap & JQuery in an angular.json file: ... "styles": [ "src/styles.css", "node_modules/bootstrap/dist/css/bootstrap.min.css" ], "scripts": [ "node_modules/jquery/dist/jquery.min.js", "node_modules/bootstrap/dist/js/bootstrap.min.js" ] ...

Let's discuss each of the above generate components and service files and we will customize it as per our requirement.

package.json This file Configures npm package dependencies that are available to all projects in the workspace. Note that angular version 7.2.0 in dependencies section in below file.

{ "name": "angular7-springboot-client", "version": "0.0.0",


"scripts": { "ng": "ng", "start": "ng serve --proxy-config proxy.conf.json", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, "private": true, "dependencies": { "@angular/animations": "~7.2.0", "@angular/common": "~7.2.0", "@angular/compiler": "~7.2.0", "@angular/core": "~7.2.0", "@angular/forms": "~7.2.0", "@angular/platform-browser": "~7.2.0", "@angular/platform-browser-dynamic": "~7.2.0", "@angular/router": "~7.2.0", "bootstrap": "^4.2.1", "core-js": "^2.5.4", "jquery": "^3.3.1", "rxjs": "~6.3.3", "tslib": "^1.9.0", "zone.js": "~0.8.26" }, "devDependencies": { "@angular-devkit/build-angular": "~0.12.0", "@angular/cli": "~7.2.1", "@angular/compiler-cli": "~7.2.0", "@angular/language-service": "~7.2.0", "@types/node": "~8.9.4", "@types/jasmine": "~2.8.8", "@types/jasminewd2": "~2.0.3", "codelyzer": "~4.5.0", "jasmine-core": "~2.99.1", "jasmine-spec-reporter": "~4.2.1", "karma": "~3.1.1", "karma-chrome-launcher": "~2.2.0", "karma-coverage-istanbul-reporter": "~2.0.1", "karma-jasmine": "~1.1.2", "karma-jasmine-html-reporter": "^0.2.2", "protractor": "~5.4.0", "ts-node": "~7.0.0", "tslint": "~5.11.0", "typescript": "~3.2.2" } }


Create Employee class - employee.ts Before defining the EmployeeListComponent, let’s define an Employee class for working with employees. create a new file employee.ts inside src/app folder and add the following code to it export class Employee { id: number; firstName: string; lastName: string; emailId: string; active: boolean; }

EmployeeService - employee-list.component.ts The EmployeeService will be used to get the data from backend by calling spring boot APIs. Update the employee.service.ts file inside src/app directory with the following code to it import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class EmployeeService { private baseUrl = '/api/v1/employees'; constructor(private http: HttpClient) { } getEmployee(id: number): Observable<Object> { return this.http.get(`${this.baseUrl}/${id}`); } createEmployee(employee: Object): Observable<Object> { return this.http.post(`${this.baseUrl}`, employee); } updateEmployee(id: number, value: any): Observable<Object> { return this.http.put(`${this.baseUrl}/${id}`, value); } deleteEmployee(id: number): Observable<any> { return this.http.delete(`${this.baseUrl}/${id}`, { responseType: 'text' }); }


getEmployeesList(): Observable<any> { return this.http.get(`${this.baseUrl}`); } }

EmployeeListComponent - employee-list.component.ts Let's update the EmployeeListComponent component which will be used to display a list of employee, create a new employee, and delete an employee. Update/remove the content of employee-list.component.ts inside src/app directory and add the following code to it import import import import

{ { { {

Observable } from "rxjs"; EmployeeService } from "./../employee.service"; Employee } from "./../employee"; Component, OnInit } from "@angular/core";

@Component({ selector: "app-employee-list", templateUrl: "./employee-list.component.html", styleUrls: ["./employee-list.component.css"] }) export class EmployeeListComponent implements OnInit { employees: Observable<Employee[]>; constructor(private employeeService: EmployeeService) {} ngOnInit() { this.reloadData(); } reloadData() { this.employees = this.employeeService.getEmployeesList(); } deleteEmployee(id: number) { this.employeeService.deleteEmployee(id) .subscribe( data => { console.log(data); this.reloadData(); }, error => console.log(error)); } }


Create a template for EmployeeListComponent - employee-list.component.html Update employee-list.component.html file with the following code to it <div class="panel panel-default"> <div class="panel-heading"> <h1>Employees</h1> </div> <div class="panel-body"> <table class="table table-striped table-bordered"> <thead> <tr> <th>Firstname</th> <th>Lastname</th> <th>Email</th> <th>Actions</th> </tr> </thead> <tbody> <tr *ngFor="let employee of employees | async"> <td>{{employee.firstName}}</td> <td>{{employee.lastName}}</td> <td>{{employee.emailId}}</td> <td><button (click)="deleteEmployee(employee.id)">Delete</button></td> </tr> </tbody> </table> </div> </div>

CreateEmployeeComponent - create-employee.component.ts CreateEmployeeComponent is used to create and handle a new employee form data. Add the following code to it import { EmployeeService } from './../employee.service'; import { Employee } from './../employee'; import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-create-employee', templateUrl: './create-employee.component.html', styleUrls: ['./create-employee.component.css'] }) export class CreateEmployeeComponent implements OnInit { employee: Employee = new Employee(); submitted = false;


constructor(private employeeService: EmployeeService) { } ngOnInit() { } newEmployee(): void { this.submitted = false; this.employee = new Employee(); } save() { this.employeeService.createEmployee(this.employee) .subscribe(data => console.log(data), error => console.log(error)); this.employee = new Employee(); } onSubmit() { this.submitted = true; this.save(); } }

Create a template for EmployeeCreateComponent createemployee.component.html

<h3>Create Employee</h3> <div [hidden]="submitted" style="width: 400px;"> <form (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="name">First Name</label> <input type="text" class="form-control" id="firstName" required [(ngModel)]="employee.firstName" name="firstName"> </div> <div class="form-group"> <label for="name">Last Name</label> <input type="text" class="form-control" id="lastName" required [(ngModel)]="employee.lastName" name="lastName"> </div> <div class="form-group"> <label for="name">First Name</label> <input type="text" class="form-control" id="emailId" required [(ngModel)]="employee.emailId" name="emailId"> </div> <button type="submit" class="btn btn-success">Submit</button> </form> </div> <div [hidden]="!submitted">


<h4>You submitted successfully!</h4> </div>

EmployeeDetailsComponent- employee-details.component.ts This component shows details of employee import { Employee } from './../employee'; import { Component, OnInit, Input } from '@angular/core'; import { EmployeeService } from '../employee.service'; import { EmployeeListComponent } from '../employee-list/employeelist.component'; @Component({ selector: 'app-employee-details', templateUrl: './employee-details.component.html', styleUrls: ['./employee-details.component.css'] }) export class EmployeeDetailsComponent implements OnInit { @Input() employee: Employee; constructor(private employeeService: EmployeeService, private listComponent: EmployeeListComponent) { } ngOnInit() { } }

Create a template for EmployeeDetailsComponent employeedetails.component.html

<div *ngIf="employee"> <div> <label>Name: </label> {{employee.firstName}} </div> <div> <label>Age: </label> {{employee.lastName}} </div> <div> <label>Active: </label> {{employee.emailId}} </div> <div> <label>Active: </label> {{employee.active}} </div>


<span class="button is-small btn-primary" *ngIf='employee.active' (click)='updateActive(false)'>Inactive</span> <span class="button is-small btn-primary" *ngIf='!employee.active' (click)='updateActive(true)'>Active</span> <span class="button is-small btn-danger" (click)='deleteEmployee()'>Delete</span> <hr/> </div>

AppRoutingModule - app-routing.module.ts

import { EmployeeDetailsComponent } from './employee-details/employeedetails.component'; import { CreateEmployeeComponent } from './create-employee/createemployee.component'; import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { EmployeeListComponent } from './employee-list/employeelist.component'; const routes: Routes = [ { path: '', redirectTo: 'employee', pathMatch: 'full' }, { path: 'employees', component: EmployeeListComponent }, { path: 'add', component: CreateEmployeeComponent }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }

AppComponent - app/app.component.ts Defines the logic for the app's root component, named AppComponent. The view associated with this root component becomes the root of the view hierarchy as you add components and services to your app. import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css']


}) export class AppComponent { title = 'Angular 7 + Spring Boot 2 + Spring Data JPA + MySQL + CRUD Tutorial'; }

app/app.component.html Defines the HTML template associated with the root AppComponent. <div class="container"> <h2>{{title}}</h2> <hr> <nav class="navbar navbar-expand-sm bg-dark navbar-dark"> <!-- Links --> <ul class="navbar-nav"> <li class="nav-item"> <a routerLink="employees" class="btn btn-primary active" role="button" routerLinkActive="active">Employees</a> </li> <li class="nav-item" style="margin-left: 10px;"> <a routerLink="add" class="btn btn-primary active" role="button" routerLinkActive="active">Add</a> </li> </ul> </nav> <router-outlet></router-outlet> </div>

app/app.module.ts Defines the root module, named AppModule, that tells Angular how to assemble the application. Initially declares only the AppComponent. As you add more components to the app, they must be declared here. import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { CreateEmployeeComponent } from './create-employee/createemployee.component'; import { EmployeeDetailsComponent } from './employee-details/employeedetails.component';


import { EmployeeListComponent } from './employee-list/employeelist.component'; import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [ AppComponent, CreateEmployeeComponent, EmployeeDetailsComponent, EmployeeListComponent ], imports: [ BrowserModule, AppRoutingModule, FormsModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }

As we already started Spring boot application. Now let's run this Angular 7 application.

Running Angular 7 Application Let's run the above developed Angular App with a command: ng serve

Output Open browser for URL http://URLalhost:4200/:


Employee List Page


Add Employee Page

Delete Employee

Note that update employee functionality is not implemented so you can take as exercise and try to implement yourself (Tip: Add an update button to the employee list page and based on employee id you can implement update functionality. Note that Rest API is already created for update employee functionality).

The source code of this article available on my GitHub repository at https://github.com/RameshMF/angular7-springboot-crud-tutorial


https://www.fullstackpython.com/object-relational-mappers-orms.html

Object-relational mappers (ORMs) An object-relational mapper (ORM) is a code library that automates the transfer of data stored in relational databases tables into objects that are more commonly used in application code.

Why are ORMs useful? ORMs provide a high-level abstraction upon a relational database that allows a developer to write Python code instead of SQL to create, read, update and delete data and schemas in their database. Developers can use the programming language they are comfortable with to work with a database instead of writing SQL statements or stored procedures. For example, without an ORM a developer would write the following SQL statement to retrieve every row in the USERS table where the zip_code column is 94107:


SELECT * FROM USERS WHERE zip_code=94107;

The equivalent Django ORM query would instead look like the following Python code: # obtain everyone in the 94107 zip code and assign to users variable users = Users.objects.filter(zip_code=94107)

The ability to write Python code instead of SQL can speed up web application development, especially at the beginning of a project. The potential development speed boost comes from not having to switch from Python code into writing declarative paradigm SQL statements. While some software developers may not mind switching back and forth between languages, it's typically easier to knock out a prototype or start a web application using a single programming language. ORMs also make it theoretically possible to switch an application between various relational databases. For example, a developer could use SQLite for local development and MySQL in production. A production application could be switched from MySQL to PostgreSQL with minimal code modifications. In practice however, it's best to use the same database for local development as is used in production. Otherwise unexpected errors could hit in production that were not seen in a local development environment. Also, it's rare that a project would switch from one database in production to another one unless there was a pressing reason. While you're learning about ORMs you should also read up on deployment and check out the application dependencies page.

Do I have to use an ORM for my web application? Python ORM libraries are not required for accessing relational databases. In fact, the low-level access is typically provided by another library called a database connector, such aspsycopg (for PostgreSQL) or MySQLpython (for MySQL). Take a look at the table below which shows how ORMs can work with different web frameworks and connectors and relational databases.


The above table shows for example that SQLAlchemy can work with varying web frameworks and database connectors. Developers can also use ORMs without a web framework, such as when creating a data analysis tool or a batch script without a user interface.

What are the downsides of using an ORM? There are numerous downsides of ORMs, including 1. Impedance mismatch 2. Potential for reduced performance 3. Shifting complexity from the database into the application code

Impedance mismatch The phrase "impedance mismatch" is commonly used in conjunction with ORMs. Impedance mismatch is a catch-all term for the difficulties that


occur when moving data between relational tables and application objects. The gist is that the way a developer uses objects is different from how data is stored and joined in relational tables. This article on ORM impedance mismatch does a solid job of explaing what the concept is at a high level and provides diagrams to visualize why the problem occurs.

Potential for reduced performance One of the concerns that's associated with any higher-level abstraction or framework is potential for reduced performance. With ORMs, the performance hit comes from the translation of application code into a corresponding SQL statement which may not be tuned properly. ORMs are also often easy to try but difficult to master. For example, a beginner using Django might not know about the select_related() function and how it can improve some queries' foreign key relationship performance. There are dozens of performance tips and tricks for every ORM. It's possible that investing time in learning those quirks may be better spent just learning SQL and how to write stored procedures. There's a lot of hand-waving "may or may not" and "potential for" in this section. In large projects ORMs are good enough for roughly 80-90% of use cases but in 10-20% of a project's database interactions there can be major performance improvements by having a knowledgeable database administrator write tuned SQL statements to replace the ORM's generated SQL code.

Shifting complexity from the database into the app code The code for working with an application's data has to live somewhere. Before ORMs were common, database stored procedures were used to encapsulate the database logic. With an ORM, the data manipulation code instead lives within the application's Python codebase. The addition of data handling logic in the codebase generally isn't an issue with a sound application design, but it does increase the total amount of Python code instead of splitting code between the application and the database stored procedures.


Python ORM Implementations There are numerous ORM implementations written in Python, including 1. 2. 3. 4. 5. 6.

SQLAlchemy Peewee The Django ORM PonyORM SQLObject Tortoise ORM There are other ORMs, such as Canonical's Storm, but most of them do not appear to currently be under active development. Learn more about the major active ORMs below.

Django's ORM The Django web framework comes with its own built-in object-relational mapping module, generally referred to as "the Django ORM" or "Django's ORM". Django's ORM works well for simple and medium-complexity database operations. However, there are often complaints that the ORM makes complex queries much more complicated than writing straight SQL or using SQLAlchemy. It is technically possible to drop down to SQL but it ties the queries to a specific database implementation. The ORM is coupled closely with Django so replacing the default ORM with SQLAlchemy is currently a hack workaround. Note though it is possible that swappable ORM backends will be possible in the future as it is now possible to change thetemplate engine for rendering output in Django. Since the majority of Django projects are tied to the default ORM, it is best to read up on advanced use cases and tools for doing your best work within the existing framework.

SQLAlchemy ORM SQLAlchemy is a well-regarded Python ORM because it gets the abstraction level "just right" and seems to make complex database queries easier to write than the Django ORM in most cases. There is an entire


page on SQLAlchemy that you should read if you want to learn more about using the library.

Peewee ORM Peewee is a Python ORM implementation that is written to be "simpler, smaller and more hackable" than SQLAlchemy. Read the full Peewee page for more information on the Python ORM implementation.

Pony Pony ORM is another Python ORM available as open source, under the Apache 2.0 license.

SQLObject ORM SQLObject is an ORM that has been under active open source development for over 14 years, since before 2003.

Schema migrations Schema migrations, for example when you need to add a new column to an existing table in your database, are not technically part of ORMs. However, since ORMs typically lead to a hands-off approach to the database (at the developers peril in many cases), libraries to perform schema migrations often go hand-in-hand with Python ORM usage on web application projects. Database schema migrations are a complex topic and deserve their own page. For now, we'll lump schema migration resources under ORM links below.

General ORM resources 

This detailed overview of ORMs is a generic description of how ORMs work and how to use them.



This example GitHub project implements the same Flask application with several different ORMs: SQLAlchemy, Peewee, MongoEngine, stdnet and PonyORM.


Martin Fowler addresses the ORM hate in an essay about how ORMs are often misused but that they do provide benefits to developers.

The Rise and Fall of Object Relational Mapping is a talk on the history of ORMs that doesn't shy away from some controversy. Overall I found the critique of conceptual ideas worth the time it took to read the presentation slides and companion text.

If you're confused about the difference between a connector, such as MySQL-python and an ORM like SQLAlchemy, read this StackOverflow answer on the topic.

What ORMs have taught me: just learn SQL is another angle in the ORM versus embedded SQL / stored procedures debate. The author's conclusion is that while working with ORMs such as SQLAlchemy and Hibernate (a Java-based ORM) can save time up front there are issues as a project evolves such as partial objects and schema redundancies. I think the author makes some valid points that some ORMs can be a shaky foundation for extremely complicated database-backed applications. However, I disagree with the overriding conclusion to eschew ORMs in favor of stored procedures. Stored procedures have their own issues and there are no perfect solutions, but I personally prefer using an ORM at the start of almost every project even if it later needs to be replaced with direct SQL queries.

The Vietnam of Computer Science provides the perspective from Ted Neward, the originator of the phrase "Object/relational mapping is the Vietnam of Computer Science" that he first spoke about in 2004. The gist of the argument against ORMs is captured in Ted's quote that an ORM "represents a quagmire which starts well, gets more complicated as time passes, and before long entraps its users in a commitment that has no clear demarcation point, no clear win conditions, and no clear exit strategy." There are follow up posts on Coding Horror and another one from Ted entitled thoughts on Vietnam commentary.

Turning the Tables: How to Get Along with your Object-Relational Mapper coins the funny but insightful phrase "database denial" to describe how some ORMs provide a usage model that can cause more issues than they solve over straight SQL queries. The post then goes into much more detail about the problems that can arise and how to mitigate or avoid them.


SQLAlchemy and Peewee resources A comprehensive list of SQLAlchemy and Peewee ORM resources can be found on their respective pages.

Django ORM links A curated list of resources can be found on the dedicated Django ORM resources page.

Pony ORM resources All Pony ORM resources are listed on the dedicated Pony ORM page.

SQLObject resources SQLObject has been around for a long time as an open source project but unfortunately there are not that many tutorials for it. The following talks and posts will get you started. If you take an interest in the project and write additional resources, file an issue ticket so we can get them added to this list. 

This post on Object-Relational Mapping with SQLObject explains the concept behind ORMs and shows the Python code for how they can be used.

Ian Bicking presented on SQLObject back in 2004 with a talk on SQLObject and Database Programming in Python.

Connecting databases to Python with SQLObject is an older post but still relevant with getting started basics.

PonyORM https://docs.ponyorm.org/queries.html

Queries


Pony provides a very convenient way to query the database using the generator expression syntax. Pony allows programmers to work with objects which are stored in a database as if they were stored in memory, using native Python syntax. It makes development much easier. For writing queries you can use Python generator expressions or lambdas.

Using Python generator expressions Pony allows to use generator expressions as a very natural way of writing database queries. Pony provides select() function which accepts Python generator, translates it to SQL and returns objects from the database. The process of the translation is described in this StackOverflow question. Here is an example of a query: query = select(c for c in Customer if sum(o.total_price for o in c.orders) > 1000)

or, with attribute lifting: query = select(c for c in Customer if sum(c.orders.total_price) > 1000)

You can apply filter() function to query query2 = query.filter(lambda person: person.age > 18)

Also you can make new query based on another query: query3 = select(customer.name for customer in query2 if customer.country == 'Canada') select() function returns an instance of a Query class, and you can then call the Query object methods for getting the result, for example: customer_name = query3.first()


From query you can return entity, attribute or tuple of arbitrary expressions select((c, sum(c.orders.total_price)) for c in Customer if sum(c.orders.total_price) > 1000)

Using lambda functions Instead of using a generator, you can write queries using the lambda function: Customer.select(lambda c: sum(c.orders.price) > 1000)

From the point of the translation the query into SQL there is no difference, if you use a generator or a lambda. The only difference is that using the lambda you can only return entity instances - there is no way to return a list of specific entity attributes or a list of tuples.

Pony ORM functions used to query the database See the Queries and functions part of the API Reference for details.

Pony query examples For demonstrating Pony queries let’s use the example from the Pony ORM distribution. You can try these queries yourself in the interactive mode and see the generated SQL. For this purpose import the example module this way: >>> from pony.orm.examples.estore import *

This module offers a simplified data model of a eCommerce online store. Here is the ER Diagram of the data model:


Here are the entity definitions: from decimal import Decimal from datetime import datetime

from pony.converting import str2datetime from pony.orm import *

db = Database()

class Customer(db.Entity):


email = Required(str, unique=True) password = Required(str) name = Required(str) country = Required(str) address = Required(str) cart_items = Set('CartItem') orders = Set('Order')

class Product(db.Entity): id = PrimaryKey(int, auto=True) name = Required(str) categories = Set('Category') description = Optional(str) picture = Optional(buffer) price = Required(Decimal) quantity = Required(int) cart_items = Set('CartItem') order_items = Set('OrderItem')

class CartItem(db.Entity): quantity = Required(int) customer = Required(Customer) product = Required(Product)


class OrderItem(db.Entity): quantity = Required(int) price = Required(Decimal) order = Required('Order') product = Required(Product) PrimaryKey(order, product)

class Order(db.Entity): id = PrimaryKey(int, auto=True) state = Required(str) date_created = Required(datetime) date_shipped = Optional(datetime) date_delivered = Optional(datetime) total_price = Required(Decimal) customer = Required(Customer) items = Set(OrderItem)

class Category(db.Entity): name = Required(str, unique=True) products = Set(Product)

set_sql_debug(True) db.bind('sqlite', 'estore.sqlite', create_db=True) db.generate_mapping(create_tables=True)


When you import this example, it will create the SQLite database in the file ‘estore.sqlite’ and fill it with some test data. Below you can see some query examples: # All USA customers Customer.select(lambda c: c.country == 'USA')

# The number of customers for each country select((c.country, count(c)) for c in Customer)

# Max product price max(p.price for p in Product)

# Max SSD price max(p.price for p in Product for cat in p.categories if cat.name == 'Solid State Drives')

# Three most expensive products Product.select().order_by(desc(Product.price))[:3]

# Out of stock products Product.select(lambda p: p.quantity == 0)

# Most popular product Product.select().order_by(lambda p: desc(sum(p.order_items.quantity))).first()


# Products that have never been ordered Product.select(lambda p: not p.order_items)

# Customers who made several orders Customer.select(lambda c: count(c.orders) > 1)

# Three most valuable customers Customer.select().order_by(lambda c: desc(sum(c.orders.total_price)))[:3]

# Customers whose orders were shipped Customer.select(lambda c: SHIPPED in c.orders.state)

# Customers with no orders Customer.select(lambda c: not c.orders)

# The same query with the LEFT JOIN instead of NOT EXISTS left_join(c for c in Customer for o in c.orders if o is None)

# Customers which ordered several different tablets select(c for c in Customer for p in c.orders.items.product if 'Tablets' in p.categories.name and count(p) > 1)


You can find more queries in the pony.orm.examples.estore module.

Query object methods See the Query result part of the API Reference for details.

Using date and time in queries You can perform arithmetic operations with the datetime and timedelta in queries. If the expression can be calculated in Python, Pony will pass the result of the calculation as a parameter into the query: select(o for o in Order if o.date_created >= datetime.now() timedelta(days=3))[:] SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped", "o"."date_delivered", "o"."total_price", "o"."customer" FROM "Order" "o" WHERE "o"."date_created" >= ?

If the operation needs to be performed with the attribute, we cannot calculate it beforehand. That is why such expression will be translated into SQL: select(o for o in Order if o.date_created + timedelta(days=3) >= datetime.now())[:] SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped", "o"."date_delivered", "o"."total_price", "o"."customer" FROM "Order" "o" WHERE datetime("o"."date_created", '+3 days') >= ?

The SQL generated by Pony will vary depending on the database. Above is the example for SQLite. Here is the same query, translated into PostgreSQL:


SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped", "o"."date_delivered", "o"."total_price", "o"."customer" FROM "order" "o" WHERE ("o"."date_created" + INTERVAL '72:0:0' DAY TO SECOND) >= %(p1)s

If you need to use a SQL function, you can use the raw_sql() function in order to include this SQL fragment: select(m for m in DBVoteMessage if m.date >= raw_sql("NOW() - '1 minute'::INTERVAL"))

With Pony you can use the datetime attributes, such as month, hour, etc. Depending on the database, it will be translated into different SQL, which extracts the value for this attribute. In this example we get the monthattribute: select(o for o in Order if o.date_created.month == 12)

Here is the result of the translation for SQLite: SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped", "o"."date_delivered", "o"."total_price", "o"."customer" FROM "Order" "o" WHERE cast(substr("o"."date_created", 6, 2) as integer) = 12

And for PostgreSQL: SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped", "o"."date_delivered", "o"."total_price", "o"."customer" FROM "order" "o" WHERE EXTRACT(MONTH FROM "o"."date_created") = 12


Automatic DISTINCT Pony tries to avoid duplicates in a query result by automatically adding the DISTINCT SQL keyword where it is necessary, because useful queries with duplicates are very rare. When someone wants to retrieve objects with a specific criteria, they typically don’t expect that the same object will be returned more than once. Also, avoiding duplicates makes the query result more predictable: you don’t need to filter duplicates out of a query result. Pony adds the DISCTINCT keyword only when there could be potential duplicates. Let’s consider a couple of examples. 1. Retrieving objects with a criteria: Person.select(lambda p: p.age > 20 and p.name == 'John')

In this example, the query doesn’t return duplicates, because the result contains the primary key column of a Person. Since duplicates are not possible here, there is no need in the DISTINCT keyword, and Pony doesn’t add it: SELECT "p"."id", "p"."name", "p"."age" FROM "Person" "p" WHERE "p"."age" > 20 AND "p"."name" = 'John'

2. Retrieving object attributes: select(p.name for p in Person)

The result of this query returns not objects, but its attribute. This query result can contain duplicates, so Pony will add DISTINCT to this query: SELECT DISTINCT "p"."name" FROM "Person" "p"


The result of a such query typically used for a dropdown list, where duplicates are not expected. It is not easy to come up with a real use-case when you want to have duplicates here. If you need to count persons with the same name, you’d better use an aggregate query: select((p.name, count(p)) for p in Person)

But if it is absolutely necessary to get all person’s names, including duplicates, you can do so by using the Query.without_distinct() method: select(p.name for p in Person).without_distinct()

3. Retrieving objects using joins: select(p for p in Person for c in p.cars if c.make in ("Toyota", "Honda"))

This query can contain duplicates, so Pony eliminates them using DISTINCT: SELECT DISTINCT "p"."id", "p"."name", "p"."age" FROM "Person" "p", "Car" "c" WHERE "c"."make" IN ('Toyota', 'Honda') AND "p"."id" = "c"."owner"

Without using DISTINCT the duplicates are possible, because the query uses two tables (Person and Car), but only one table is used in the SELECT section. The query above returns only persons (and not their cars), and therefore it is typically not desirable to get the same person in the result more than once. We believe that without duplicates the result looks more intuitive. But if for some reason you don’t need to exclude duplicates, you always can add without_distinct() to the query: select(p for p in Person for c in p.cars if c.make in ("Toyota", "Honda")).without_distinct()


The user probably would like to see the Person objects duplicates if the query result contains cars owned by each person. In this case the Pony query would be different: select((p, c) for p in Person for c in p.cars if c.make in ("Toyota", "Honda"))

And in this case Pony will not add the DISTINCT keyword to SQL query. To summarize: 1. The principle “all queries do not return duplicates by default” is easy to understand and doesn’t lead to surprises. 2. Such behavior is what most users want in most cases. 3. Pony doesn’t add DISTINCT when a query is not supposed to have duplicates. 4. The query method without_distinct() can be used for forcing Pony do not eliminate duplicates.

Functions which can be used inside a query Here is the list of functions that can be used inside a generator query:              

avg() abs() exists() len() max() min() count() concat() group_concat() random() raw_sql() select() sum() getattr()

Examples: select(avg(c.orders.total_price) for c in Customer)


SELECT AVG("order-1"."total_price") FROM "Customer" "c" LEFT JOIN "Order" "order-1" ON "c"."id" = "order-1"."customer" select(o for o in Order if o.customer in select(c for c in Customer if c.name.startswith('A')))[:] SELECT "o"."id", "o"."state", "o"."date_created", "o"."date_shipped", "o"."date_delivered", "o"."total_price", "o"."customer" FROM "Order" "o" WHERE "o"."customer" IN ( SELECT "c"."id" FROM "Customer" "c" WHERE "c"."name" LIKE 'A%' )

Using getattr() getattr() is a built-in Python function, that can be used for getting the attribute value.

Example: attr_name = 'name' param_value = 'John' select(c for c in Customer if getattr(c, attr_name) == param_value)


Using raw SQL Pony allows using raw SQL in your queries. There are two options on how you can use raw SQL: 1. Use the raw_sql() function in order to write only a part of a generator or lambda query using raw SQL. 2. Write a complete SQL query using the Entity.select_by_sql() or Entity.get_by_sql() methods.

Using the raw_sql() function Let’s explore examples of using the raw_sql() function. Here is the schema and initial data that we’ll use for our examples: from datetime import date from pony.orm import *

db = Database('sqlite', ':memory:')

class Person(db.Entity): id = PrimaryKey(int) name = Required(str) age = Required(int) dob = Required(date)

db.generate_mapping(create_tables=True)

with db_session:


Person(id=1, name='John', age=30, dob=date(1986, 1, 1)) Person(id=2, name='Mike', age=32, dob=date(1984, 5, 20)) Person(id=3, name='Mary', age=20, dob=date(1996, 2, 15))

The raw_sql() result can be treated as a logical expression: select(p for p in Person if raw_sql('abs("p"."age") > 25'))

The raw_sql() result can be used for a comparison: q = Person.select(lambda x: raw_sql('abs("x"."age")') > 25) print(q.get_sql())

SELECT "x"."id", "x"."name", "x"."age", "x"."dob" FROM "Person" "x" WHERE abs("x"."age") > 25

Also, in the example above we use raw_sql() in a lambda query and print out the resulting SQL. As you can see the raw SQL part becomes a part of the whole query. The raw_sql() can accept $parameters: x = 25 select(p for p in Person if raw_sql('abs("p"."age") > $x'))

You can change the content of the raw_sql() function dynamically and still use parameters inside: x = 1 s = 'p.id > $x' select(p for p in Person if raw_sql(s))

Another way of using dynamic raw SQL content:


x = 1 cond = raw_sql('p.id > $x') select(p for p in Person if cond)

You can use various types inside the raw SQL query: x = date(1990, 1, 1) select(p for p in Person if raw_sql('p.dob < $x'))

Parameters inside the raw SQL part can be combined: x = 10 y = 15 select(p for p in Person if raw_sql('p.age > $(x + y)'))

You can even call Python functions inside: select(p for p in Person if raw_sql('p.dob < $date.today()'))

The raw_sql() function can be used not only in the condition part, but also in the part which returns the result of the query: names = select(raw_sql('UPPER(p.name)') for p in Person)[:] print(names)

['JOHN', 'MIKE', 'MARY']

But when you return data using the raw_sql() function, you might need to specify the type of the result, because Pony has no idea on what the result type is: dates = select(raw_sql('(p.dob)') for p in Person)[:] print(dates)

['1985-01-01', '1983-05-20', '1995-02-15']


If you want to get the result as a list of dates, you need to specify the result_type: dates = select(raw_sql('(p.dob)', result_type=date) for p in Person)[:] print(dates)

[datetime.date(1986, 1, 1), datetime.date(1984, 5, 20), datetime.date(1996, 2, 15)]

The raw_sql() function can be used in a Query.filter() too: x = 25 select(p for p in Person).filter(lambda p: p.age > raw_sql('$x'))

It can be used inside the Query.filter() without lambda. In this case you have to use the first letter of entity name in lower case as the alias: x = 25 Person.select().filter(raw_sql('p.age > $x'))

You can use several raw_sql() expressions in a single query: x = '123' y = 'John' Person.select(lambda p: raw_sql("UPPER(p.name) || $x") == raw_sql("UPPER($y || '123')"))

The same parameter names can be used several times with different types and values: x = 10 y = 31 q = select(p for p in Person if p.age > x and p.age < raw_sql('$y')) x = date(1980, 1, 1)


y = 'j' q = q.filter(lambda p: p.dob > x and p.name.startswith(raw_sql('UPPER($y)'))) persons = q[:]

You can use raw_sql() in Query.order_by() section: x = 9 Person.select().order_by(lambda p: raw_sql('SUBSTR(p.dob, $x)'))

Or without lambda, if you use the same alias, that you used in previous filters. In this case we use the default alias - the first letter of the entity name: x = 9 Person.select().order_by(raw_sql('SUBSTR(p.dob, $x)'))

Using the select_by_sql() and get_by_sql() methods Although Pony can translate almost any condition written in Python to SQL, sometimes the need arises to use raw SQL, for example - in order to call a stored procedure or to use a dialect feature of a specific database system. In this case, Pony allows the user to write a query in a raw SQL, by placing it inside the function Entity.select_by_sql() or Entity.get_by_sql() as a string: Product.select_by_sql("SELECT * FROM Products")

Unlike the method Entity.select(), the method Entity.select_by_sql() does not return the Queryobject, but a list of entity instances. Parameters are passed using the following syntax: “$name_variable” or “$(expression in Python)”. For example: x = 1000 y = 500 Product.select_by_sql("SELECT * FROM Product WHERE price > $x OR price = $(y * 2)")


When Pony encounters a parameter within a raw SQL query, it gets the variable value from the current frame (from globals and locals) or from the dictionaries which can be passed as parameters: Product.select_by_sql("SELECT * FROM Product WHERE price > $x OR price = $(y * 2)", globals={'x': 100}, locals={'y': 200})

Variables and more complex expressions specified after the $ sign, will be automatically calculated and transferred into the query as parameters, which makes SQL-injection impossible. Pony automatically replaces $x in the query string with “?”, “%S” or with other paramstyle, used in your database. If you need to use the $ sign in the query (for example, in the name of a system table), you have to write two $signs in succession: $$. https://www.fullstackpython.com/pony-orm.html

Curso de Kotlin: Tipos de Variables https://fabrica-software.blogspot.com/2018/11/curso-kotlin-tipos-de-variables.html

Tabla de Contenido:

1. Introducción 1. Sintaxis para definir variables 2. Números 1. Definición de valores numéricos 2. Uso de _ en valores numéricos (Desde versión 1.1) 3. Representando valores numéricos que pueden ser nulos 3. Caracteres (Char) 4. Cadenas de Texto (String) 1. Tipos de String y Definición de Valores


2. Expresiones en Strings (String Templates) 5. Variables Lógicas (Boolean) 6. Arrays 7. Conversión de Tipos 8. Fuentes y Enlaces de interés

Introducción: En Kotlin, todo es un objeto, en el sentido en que podemos llamar a funciones y propiedades en cada variable. Algunos de los tipos pueden tener una representación interna especial – por ejemplo, números, caracteres y lógicos (boolean), se pueden representar como valores primitivos en el modo de ejecución (runtime) – Pero, para los mortales parecen como clases ordinarias. En esta sección vamos a describir los tipos básicos usados en Kotlin: núm eros, caracteres, cadenas de texto, variables lógicos (booleans) y arrays.

A p re n di en do lo s t ipo s bá si co s d e va riab l es en ko tl in . . . - T ui te alo

Sintaxis para definir variables: val Identificador[: TipoVariable[?]] = Valor ; var Identificador[[: TipoVariable[?]] [= Valor]];


Vamos a explicar estas sentencias: val, son variables de solo lectura, como constantes en Java, es decir, no se puede modificar su valor. var, son variables mutables, es decir, que se puede modificar su valor. Identificador, es el nombre que queramos asignar a la variable. : TipoVariable, opcional, es el tipo de variable que estamos asignando, numero, carácter, boleano, etc. ?, opcional, sirve para hacer referencia de números u otro tipo de variable que pueden contener nulos. = Valor, asignación de valor a la hora de declarar una variable ( opcional en los var ).

l as va ri ab le s va l so n de so lo le c tu ra , la s va ri ab le s v ar so n d e l ec tu ra y es cr itu ra . . . - Tu it ea lo

Números: Kotlin maneja los números de una forma muy parecida a Java, pero no exactamente igual. Por ejemplo, no existen conversiones implícitas de números, y los literales son ligeramente diferentes en algunos casos.

Estos son los tipos de variables que representan a números en Kotlin (similares a Java):


Tipo

Tamaño en Bits

Double 64 Float

32

Long

64

Int

32

Short

16

Byte

8

Ko tl i n ma ne ja lo s núm e ro s d e u na f o rma m uy pa re ci da a Jav a , p e ro no e x ac tam e nt e i gua l. . . - T ui tea lo

Definición de valores numéricos: Existen diferentes tipos de literales numéricos:

para def inir valores

Decimales: 1234 o Long, se etiquetan con una L mayuscula: 123L  Hexadecimales: 0x0F  Binarios: 0b00001011 NOTA: Para los octales, no están soportados los literales de este tipo. 

Doubles, por defecto: 123.5, 123.5e10 ( 123.5 elevado a 10

). Floats, se etiquetan con una f minúscula o F mayúscula: 123.5f o 123.5F 

Uso de _ en valores numéricos (Desde versión 1.1):

Se pueden utilizar las barras bajas _ para hacer los valores numéricos más legibles y comprensibles:


val oneMillion = 1_000_000 val creditCardNumber = 1234_5678_9012_3456L // (Etiquetado como v ariable Long). val socialSecurityNumber = 999_99_9999L // (Etiquetado como varia ble Long). val hexBytes = 0xFF_EC_DE_5E val bytes = 0b11010010_01101001_10010100_10010010

Se pueden utilizar las barras bajas _ para hacer los valores numéricos más legibles y comprensibles... - Tuitealo Representando valores numéricos que pueden ser nulos:

En Kotlin, se pueden representar valores numéricos que pueden hacer referencia a valores nulos. Para ello, se utiliza el signo interrogación ?. En estos casos, se dice que los números están encapsulados (boxed). Ej: val a: Int? = 1 Caracteres (Char):

Los caracteres se representan como Char. No pueden ser tratados directamente como números. La definición del valor de un carácter va entrecomillado, con comilla simple: ‘H’.

Se pueden utilizar caracteres especiales utilizando delante una contra barra \. Los caracteres especiales que están soportados actualmente son:

\t, \b, \n, \r, \', \", \\ y \$

Para utilizar otro carácter especial no incluido, se puede utilizar el código Unicode con la contra barra. Por ejemplo: '\u0026' que sería el signo ampersan &.


Cadenas de Texto (String):

Las cadenas de texto se representan como String, a partir de ahora nos referiremos a las cadenas de texto como Strings.

Los strings son inmutables, es decir, que su valor no se puede cambiar. Los elementos de un string son caracteres y se puede acceder a ellos a través del operador de indexado: str[i]. Un string se puede iterar con un bucle for, que veremos más adelante.

Se pueden concatenar strings con el operador +. También funciona a la hora de concatenar strings con valores de otros tipos (Números, Caracteres, etc). OJO, esto funciona, siempre y cuando, el primer elemento de la concatenación sea un string, si no, al ejecutar la aplicación, recibiremos una excepción (más adelante veremos el tratamiento de excepciones).

val s = "abc" + 1 println(s + "def")

No obstante, en la mayoría de los casos, usar expresiones en string o strings crudas (raw strings) es preferible a la concatenación de string.

Los Strings son inmutables, no pueden cambiar su contenido y están compuestos por caracteres... - Tuitealo


Tipos de String y Definición de Valores:

Kotlin tiene dos opciones a la hora de definir valores a los strings: Escaped strings: Son cadenas en las que debe contener caracteres de escape en ella. Son muy parecidas a los strings de java. 

val s = "Hello, world!\n"

El carácter de escape es la contra barra, como vimos en los tipos Char, para definir caracteres especiales, en este caso \n, new line. Raw Strings: Son cadenas que pueden contener líneas de texto y otros caracteres, sin caracteres de escape. Estos strings se definen con triple doble comillas: """. 

val text = """ for (c in "foo") print(c) """

Expresiones en Strings (String Templates):

En Kotlin, los strings pueden contener “template expressions” que son piezas de código que son evaluadas y cuyo resultado es concatenado en el string. Con mis propias palabras: Son modos de imprimir dentro de una cadena de texto valores de variables, operaciones matemáticas o llamadas a funciones dentro de ellas. Una “template expression” dólar $ seguida de:

empieza

con

el

signo

del

El nombre de una variable. Ej: “i = $i”  Una operación matemática o la llamada a un método entre llaves ${expresión}. 


val i = 10 println("i = $i") // prints "i = 10" val s = "abc" println("$s.length is ${s.length}") // prints "abc.length is 3"

Estas expresiones se pueden usar en los dos tipos de strings mencionados anteriormente (Escaped Strings y Raw Strings). Si se necesita representar el signo dólar $ en una raw string, se debe usar el siguiente literal (ya que no se pueden usar caracteres de escape): val price = """ ${'$'}9.99 """

L a m ejo r f o rma d e co n cat e nar S tr i ng s es co n e x pr es io n es q ue co m ie n za n co n e l si gno $ . .. - Tu it ea lo

Variables Lógicas (Boolean): Las variables de tipo lógico se representan como Boolean y solo tienen dos valores posibles, verdadero y falso ( true or false).

Las variables booleanas también se pueden (boxed) si se necesita una referencia de valor nulo.

encapsular

Operadores que se pueden utilizar con variables booleanas: || - Des-conjunción, se puede traducir como: O. X es verdadero “O” Y es verdadero.


&& - Conjunción, se puede traducir como: Y. X es verdadero “Y” Z es verdadero. ! - Negación.

En próximos artículos hablaremos de los operadores más en detalle.

Arrays: Los arrays en Kotlin se representan como la clase Array, que tiene funciones get y set (que tornan entre corchetes [] por convención) y la propiedad size entre otras funciones que son de utilidad. Los arrays en Kotlin no pueden variar de tipo (de un array de enteros a Otro cualquiera).

class Array<T> private constructor() { val size: Int operator fun get(index: Int): T operator fun set(index: Int, value: T): Unit operator fun iterator(): Iterator<T> // ... }

Para crear un array, podemos usar la función arrayOf() y pasando los valores de los items, entonces con arrayOf(1, 2, 3) creamos el array siguiente: [1, 2, 3]. También se puede crear un array con valores nulos con la función arrayOfNulls() indicando el tamaño del array, que será rellenado con valores nulos.

Otra opción es usar el constructor Array, que obtiene el tamaño del array. Esta función puede devolver el valor inicial de cada elemento dando el indice del mismo.


// Creates an Array<String> with values ["0", "1", "4", "9", "16" ] val asc = Array(5, { i -> (i * i).toString() }) asc.forEach { println(it) }

Kotlin también tiene clases especiales para representar arrays de tipos primitivos como: ByteArray, ShortArray, IntArray, etc.

L o s a r ray s no pu e de n c amb iar de t ipo .. . - Tu it ea lo

Conversión de Tipos: Como apuntábamos antes, en Kotlin no existe la posibilidad de convertir tipos de forma implícita. También comentamos que en Kotlin todo son objetos y las variables también tienen métodos. Entre estos métodos, tenemos los necesarios para poder hacer conversiones de tipos:       

toByte(): Byte toShort(): Short toInt(): Int toLong(): Long toFloat(): Float toDouble(): Double toChar(): Char

Fuentes y Enlaces de interés: Documentación oficial de Kotlin con códigos de ejemplo ejecutables:


https://kotlinlang.org/docs/reference/basic-types.html

Testea tu código Kotlin en la pagina oficial Online: https://play.kotlinlang.org/

Referencia completa de Kotlin (PDF de la página oficial): https://kotlinlang.org/docs/kotlin-docs.pdf

Tabla de caracteres unicode en castellano: https://unicode-table.com/es/

Sintaxis Kotlin: Declaración de Variables https://fabrica-software.blogspot.com/2018/12/sintaxis-kotlin-declaracion-de-variables.html

Aprende la sintaxis básica de la declaración de variables en kotlin.

val Identificador[: TipoVariable[?]] = Valor; var Identificador[[: TipoVariable[?]] [= Valor]];

Vamos a explicar estas sentencias: val, son variables de solo lectura, como constantes en Java, es decir, no se puede modificar su valor. var, son variables mutables, es decir, que se puede modificar su valor. Identificador, es el nombre que queramos asignar a la variable. : TipoVariable, opcional, es el tipo de variable que estamos asignando, numero, carácter, boleano, etc.


?, opcional, sirve para hacer referencia de números u otro tipo de variable que pueden contener nulos. = Valor, asignación de valor a la hora de declarar una variable ( opcional en los var ).

https://fabrica-software.blogspot.com/2018/12/curso-kotlin-operadores.html

Curso Kotlin: Operadores Introducción a los Operadores: En este articulo sobre los operadores en kotlin, vamos a ver los diferentes tipos de operadores existentes: operadores de asignación, operadores aritméticos, operadores lógicos, operadores de comparación, operadores de igualdad y desigualdad, etc. Antes de continuar, ¿Que es un operador? Un operador es un símbolo o función que determina el tipo de operación o relaciónentre, el o los, operandos de una expresión.

Tabla de Contenido:

1. Introducción a los Operadores 2. Operadores de Asignación 1. Ejemplos Operadores de Asignación 3. Operadores Aritméticos 1. Ejemplos Operadores Aritméticos 4. Operadores Lógicos 1. Ejemplos Operadores Lógicos 5. Operadores de Comparación 1. Ejemplos Operadores de Comparación 6. Operadores a nivel de Bit


1. Ejemplos Operadores a nivel de Bit 7. Operadores de Igualdad y Desigualdad 8. Conclusión 9. Referencias

Operadores de Asignación: Los operadores de asignación sirven para, como su nombre indica, asignar valores a variables. Entre estos operadores, también encontramos los operadores de aumento o decremento de valores. Operador

Descripción

=

Operador de Asignación. También se usa para asignar valores por d funciones.

+=

Operador de asignación con incremento o suma de operandos. Tam concatenar strings, aunque como vimos, lo mejor es usar plantillas

-=

Operador de asignación con decremento o resta de operandos.

*=

Operador de asignación cuyo resultado es la multiplicación de oper

/=

Operador de asignación cuyo resultado es la división de operandos.

%=

Operador de asignación cuyo resultado es el resto de la división de

Ejemplos Operadores de Asignación: Para ver como funcionan los operadores de Asignación, vamos


a poner un par de ejemplos:

Operador de asignación: += // Ejemplos de asignación += var x: Int = 2 x += x // es igual a: x = x + x. Resultado: 4. var x: Int = 1 x += x // es igual a: x = x + x. Resultado: 2. // Ejemplos de asignación += con Strings. var x: String = "Hola" x += " Mundo" // es igual a: x = x + " Mundo". Resultado: "Hola M undo".

Operador de asignación: -= // Ejemplos de asignación -= var x: Int = 1 x -= x // es igual a: x = x - x. Resultado: 0. var x: Int = 4 x -= 2 // es igual a: x = x - 2. Resultado: 2.

Operador de asignación: *= // Ejemplos de asignación *= var x: Int = 1 x *= 5 // es igual a: x = x * 5. Resultado: 5. var x: Int = 4 x *= x // es igual a: x = x * x, o, x elevado a 2. Resultado: 16.

Operador de asignación: /= // Ejemplos de asignación /= var x: Int = 4 x /= 2 // es igual a: x = x / 2. Resultado: 2. var x: Int = 16 x /= x // es igual a: x = x / x. Resultado: 1.

Operador de asignación: %= // Ejemplos de asignación %= var x: Int = 4 x %= 2 // es igual a: x = x % 2. Resultado: 0. var x: Float = 4.5F


x %= 2 // es igual a: x = x % 2. Resultado: 0.5

Operadores Aritméticos: Los operadores aritméticos sirven para realizar operaciones matemáticas entre operandos numéricos. El operador aritmético +, como vimos en el el articulo sobre tipos de variables, cadenas de texto (string), también sirve para concatenar strings, aunque es preferible utilizar las plantillas de strings. Operador

Descripción

+

Operador Aritmético + Suma de operandos o signo positivo. Tambi concatenación en Strings.

-

Operador Aritmético - Resta de operandos o signo negativo.

*

Operador Aritmético * Multiplicación de operandos. También se us parametro vararg.

/

Operador Aritmético / División de operandos.

%

Operador Aritmético % Resto de la división de operandos.

Ejemplos Operadores Aritméticos: Vamos a incluir unos ejemplos de uso:

Operador aritmético: + // Ejemplo de suma entre 2 enteros. var x: Int = 4 var y: Int = 6 var suma: Int suma = x + y // Resultado: 10. // Ejemplo de concatenación de dos String:


var cadena1: String = "Hola" var cadena2: String = "Mundo" var concat: String concat = cadena1 + " " + cadena2 // Resultado: "Hola Mundo".

Operador aritmético: // Ejemplo de resta entre 2 enteros. var x: Int = 4 var y: Int = 6 var resta: Int resta = x - y // Resultado: -2.

Operador aritmético: * // Ejemplo de multiplicación entre 2 enteros. var x: Int = 4 var y: Int = 6 var multiplicacion: Int multiplicacion = x * y // Resultado: 24.

Operador aritmético: / // Ejemplo de división entre 2 enteros. var x: Int = 4 var y: Int = 6 var division: Int division = x / y // Resultado: 0. La variable division es entero, por eso no recoge los decimales. // Ejemplo de división entre 2 Floats. var x: Float = 4F var y: Float = 6F var division: Float division = x / y // Resultado: 0.6666667 Ahora si vemos el result ado con los decimales.

Operador aritmético: % // Ejemplo de resto de la división entre 2 enteros. var x: Int = 4 var y: Int = 6 var resto: Int resto = x % y // Resultado: 4.

Operadores Lógicos: Existen 3 operadores lógicos en Kotlin:


Operador de conjunción && , traducido como y. También se puede usar la palabra clave and .  Operador de disyunción || , traducido como o. También se puede usar la palabra clave or .  Operador de negación ! , traducido como no. 

Ejemplos Operadores Lógicos: Veamos unos ejemplos completos, con todas las posibilidades de uso, de estos operadores lógicos:

Operador de Conjunción: && // Ejemplo de conjunciones &&: var x: Boolean = false var y: Boolean = false var conjuncion: Boolean conjuncion = x && y // Resultado: false var x: Boolean = false var y: Boolean = true var conjuncion: Boolean conjuncion = x && y // Resultado: false var x: Boolean = true var y: Boolean = false var conjuncion: Boolean conjuncion = x && y // Resultado: fa lse var x: Boolean = true var y: Boolean = true var conjuncion: Boolean conjuncion = x && y // Resultado: true

Como podemos observar, en la conjunción, se deben cumplir ( Deben ser verdaderos ) los dos operandos.

Operador de Disyunción: || // Ejemplo de disyunciones ||: var x: Boolean = false var y: Boolean = false var disyuncion: Boolean disyuncion = x || y // Resultado: false


var x: Boolean = false var y: Boolean = true var disyuncion: Boolean disyuncion = x || y // Resultado: true var x: Boolean = true var y: Boolean = false var disyuncion: Boolean disyuncion = x || y // Resultado: true var x: Boolean = true var y: Boolean = true var disyuncion: Boolean disyuncion = x || y // Resultado: true

En las disyunciones, a diferencia de las conjunciones, con que se cumpla un operando nos resulta verdadero.

Operador negación: ! // Ejemplo de negación !: var x: Boolean = false var negacion: Boolean negacion = !x // Resultado: true var x: Boolean = true var negacion: Boolean negacion = !x // Resultado: false

Con la negación, invertimos el valor booleano de una variable, como se puede ver en los ejemplos.

Operadores de Comparación: Los operadores de comparación nos sirven para comparar 2 operandos. En kotlin, todas las comparaciones se traducen en llamadas al método compareTo() , en los tipos no primitivos, el cual retorna un entero Int . Expresión A<B

Traducido a

Descripci

A.compareTo(B) < 0

Operador de comparación A menor


Expresión

Traducido a

Descripci

A.compareTo(B) > 0

Operador de comparación A mayor

A <= B

A.compareTo(B) <= 0

Operador de comparación A menor

A >= B

A.compareTo(B) >= 0

Operador de comparación A mayor

A>B

Para las variables de tipo Float o Double , cuando son tipificadas como tal, se pueden realizar comparaciones con rangos. A continuación, mostramos las expresiones de comparación de rangos para las variables de tipo Float o Double : Expresión

Descripción

X in A..B

Operador de comparación de rango, donde X está entre A (inicio d rango).

X !in A..B

Operador de comparación de rango, donde X NO está entre A (inic rango).

Ejemplos Operadores de Comparación: Operador de comparación: < // Ejemplo operador de comparación < var x: Int = 4 var y: Int = 6 var comparacion: Boolean comparacion = x < y // Calculado: 4 < 6, resultado: true. var x: Float = 4.1F var y: Float = 4F var comparacion: Boolean comparacion = x < y // Calculado: 4.1 < 4, resultado: false. var x: Char = 'A' var y: Char = 'a' var comparacion: Boolean comparacion = x < y // Calculado: ('A' = 65) < ('a' = 97), result ado: true.


// El ejemplo anterior se podría tipificar mejor, del siguiente m odo: var x: Int = 'A'.toInt() var y: Int = 'a'.toInt() var comparacion: Boolean comparacion = x < y // Calculado: 65 < 97, resultado: true.

Operador de comparación: > // Ejemplo operador de comparación > var x: Int = 4 var y: Int = 6 var comparacion: Boolean comparacion = x > y // Calculado: 4 > 6, resultado: false. var x: Float = 4.1F var y: Float = 4F var comparacion: Boolean comparacion = x > y // Calculado: 4.1 > 4, resultado: true. var x: Char = 'A' var y: Char = 'a' var comparacion: Boolean comparacion = x > y // Calculado: ('A' = 65) > ('a' = 97), result ado: false. // El ejemplo anterior se podría tipificar mejor, del siguiente m odo: var x: Int = 'A'.toInt() var y: Int = 'a'.toInt() var comparacion: Boolean comparacion = x > y // Calculado: 65 > 97, resultado: false.

Operador de comparación: <= // Ejemplo operador de comparación &lt=; var x: Int = 4 var y: Int = 6 var comparacion: Boolean comparacion = x <= y // Calculado: 4 <= 6, resultado: true. var x: Float = 4.1F var y: Float = 4F var comparacion: Boolean comparacion = x <= y // Calculado: 4.1 <= 4, resultado: false. var x: Char = 'A' var y: Char = 'a' var comparacion: Boolean comparacion = x <= y // Calculado: ('A' = 65) <= ('a' = 97), resu ltado: true. // El ejemplo anterior se podría tipificar mejor, del siguiente m odo:


var x: Int = 'A'.toInt() var y: Int = 'a'.toInt() var comparacion: Boolean comparacion = x <= y // Calculado: 65 <= 97, resultado: true.

Operador de comparación: >= // Ejemplo operador de comparación >= var x: Int = 4 var y: Int = 6 var comparacion: Boolean comparacion = x >= y // Calculado: 4 >= 6, resultado: false. var x: Float = 4.1F var y: Float = 4F var comparacion: Boolean comparacion = x >= y // Calculado: 4.1 >= 4, resultado: true. var x: Char = 'A' var y: Char = 'a' var comparacion: Boolean comparacion = x >= y // Calculado: ('A' = 65) >= ('a' = 97), resu ltado: false. // El ejemplo anterior se podría tipificar mejor, del siguiente m odo: var x: Int = 'A'.toInt() var y: Int = 'a'.toInt() var comparacion: Boolean comparacion = x >= y // Calculado: 65 >= 97, resultado: false.

Operadores a nivel de Bit: Kotlin soporta operaciones a nivel de Bit, sobre variables tipo Int y Long únicamente. Sobre estos operadores, no existen caracteres especiales, pero si funciones que pueden ser invocadas entre dos operandos. A continuación, veremos la tabla con estos operadores y una descripción de su funcionamiento: Función shl(bits)

Descripción

Función de operador a nivel de Bits shl, desplazamiento hacia la izq signo (+ positivo o - negativo).


Función

Descripción

shr(bits)

Función de operador a nivel de Bits shr, desplazamiento hacia la de signo (+ positivo o - negativo).

ushr(bits)

Función de operador a nivel de Bits ushr, desplazamiento hacia la d signo (+ positivo o - negativo).

and(bits) Función de operador a nivel de Bits and (y). or(bits) Función de operador a nivel de Bits or (o). xor(bits) Función de operador a nivel de Bits xor (o exclusivo). inv()

Función de operador a nivel de Bits inv (inversión).

Ejemplos Operadores a nivel de Bit: Vamos a ver unos ejemplos de uso para estas funciones que, nos hacen de operadores a nivel de bit:

Operador a nivel de Bit: shl(bits) // Ejemplo "operadores" a nivel de Bit shl(bits): var x: Int = 2 var desplazamiento: Int = 4 var resultado: Int resultado = x shl desplazamiento // calculado: 2 shl 4, resultado : 32 /* calculo, paso a paso, del resultado: * 2 en binario: 10 * Bits de Desplazamiento: 4 (desplazamiento de x hacia la izquie rda, rellenando con 4 ceros delante.). * resultado en binario de desplazar 2, 4 posiciones a la izquier da: 100000 * resultado en decimal: 32 */

Operador a nivel de Bit: shr(bits) // Ejemplo "operadores" a nivel de Bit shr(bits) var x: Int = -32 var desplazamiento: Int = 2


var resultado: Int resultado = x shr desplazamiento // calculado: -32 shr 2, resulta do: -8 /* calculo, paso a paso, del resultado: * -32 en binario: 100000, con el bit de signo en 1 (negativo), e l bit de signo positivo es 0. * Bits de Desplazamiento: 2 (desplazamiento de x hacia la derech a, rellenando con 2 ceros detrás). * resultado en bi nario de desplazar -32, 2 posiciones a la izqui erda: 001000 (quitamos los 2 últimos dígitos de la derecha). * resultado en decimal: -8 */

Operador a nivel de Bit: ushr(bits) // Ejemplo "operadores" a nivel de Bit ushr(bits) var x: Int = 30 var desplazamiento: Int = 2 var resultado: Int resultado = x ushr desplazamiento // calculado: 30 ushr 2, result ado: 7 /* calculo, paso a paso, del resultado: * 30 en binario: 11110 * Bits de Desplazamiento: 2 (desplazamiento de x hacia la derech a, rellenando con 2 ceros detrás). * resultado en binario de desplazar 30, 2 posiciones a la izquie rda: 00111 (quitamos los 2 últimos dígitos de la derecha). * resultado en decimal: 7 */

Operador a nivel de Bit: and(bits) // Ejemplo "operadores" a nivel de Bit a nd(bits) var x: Int = 5 var y: Int = 3 var resultado: Int resultado = x and y // calculado: 5 y 3, resultado: 1.

Vamos a explicar en detalle como funciona el operador and(bits) : El operador and bit a bit, toma dos números enteros y realiza la operación and lógica en cada par correspondiente de bits. El resultado en cada posición es 1 si el bit correspondiente de los dos operandos es 1, y 0 de lo contrario. Os pongo una tabla, con el calculo anterior x = 5 e y = 3:


X

Y

Resultado AND

1

0

0

0

1

0

1

1

1

Como podemos ver, el resultado de operar AND entre 5 y 3 en binario es 001, que se traduce en decimal como 1.

Operador a nivel de Bit: or(bits) // Ejemplo "operadores" a nivel de Bit or(bits) var x: Int = 5 var y: Int = 3 var resultado: Int resultado = x or y // calculado: 5 y 3, resultado: 7.

Vamos a explicar en detalle como funciona el operador or(bits) : Una operación or bit a bit, toma dos números enteros y realiza la operación or inclusivo en cada par correspondiente de bits. El resultado en cada posición es 1 si el bit correspondiente de cualquiera de los dos operandos es 1, y 0 si ambos bits son 0 . Os pongo una tabla, con el calculo anterior x = 5 e y = 3: X

Y

Resultado OR

1

0

1

0

1

1

1

1

1


Como podemos ver, el resultado de operar OR entre 5 y 3 en binario es 111, que se traduce en decimal como 7.

Operador a nivel de Bit: xor(bits) // Ejemplo "operadores" a nivel de Bit xor(bits) var x: Int = 5 var y: Int = 3 var resultado: Int resultado = x xor y // calculado: 5 y 3, resultado: 6.

Vamos a explicar en detalle como funciona el operador xor(bits) : Una operación xor bit a bit, toma dos números enteros y realiza la operación or exclusivo en cada par correspondiente de bits. El resultado en cada posición es 1 si el par de bits son diferentes y cero si el par de bits son iguales. Os pongo una tabla, con el calculo anterior x = 5 e y = 3: X

Y

Resultado XOR

1

0

1

0

1

1

1

1

0

Como podemos ver, el resultado de operar XOR entre 5 y 3 en binario es 110, que se traduce en decimal como 6.

Operador a nivel de Bit: inv() // Ejemplo "operadores" a nivel de Bit inv() var x: Int = -6 var resultado: Int


resultado = (x.inv() + 1) // calculado: inverso de -6 + 1, result ado: 6 var x: Int = 6 var resultado: Int resultado = (x.inv() + 1) // calculado: inverso de 6 + 1, resulta do: -6

* En la función de invertir bit a bit, no estoy 100% seguro de como opera. Pero añadiendo ese + 1, obtendremos el mismo número introducido con el signo (positivo o negativo) invertido.

Operadores de Igualdad y Desigualdad: Estos operadores de igualdad y desigualdad, nos permite comprobar si los operandos son equivalentes (para variables no primitivas, se traduce en la llamada al método equals() ). Los operadores de igualdad y desigualdad son los siguientes: Expresión

Traducción:

x == y

x?.equals(y) ?: (y === null)

Operador de ig

x != y

!(x?.equals(y) ?: (y === null))

Operador de d

La operación == se traduce en una expresión compleja que, también cubre los valores nulos null . null == null es siempre verdadero ( true ) y x == null para una variable x que no es nula, el resultado será siempre falso ( false ) y no invocará la función x.equals() . También podemos realizar operaciones de igualdad y desigualdad de referencias. La igualdad de referencias se representa con el operador === y la desigualdad de referencias se representa con el operador !== . La expresión x === y ,


resulta verdadero ( true ) si, y solo si, x e y apuntan al mismo objeto. Para valores que se representan en tiempo de ejecución como variables primitivas (por ejemplo, Int ), el operador === se traduce al operador == y para la desigualdad, !== pasa lo mismo, se traduce a != .

Conclusión: En este articulo, hemos revisado los operadores, y funciones que trabajan como operadores, disponibles en kotlin. Si a la hora de leer este artículo te surge alguna duda, falta algún operador, conoces la forma de operar de la función inv(), etc. puedes dejar un comentario. Si te ha gustado el articulo, compártelo con tus amigos y/o en tus redes sociales.

Curso Kotlin: Operadores Introducción a los Operadores: En este articulo sobre los operadores en kotlin, vamos a ver los diferentes tipos de operadores existentes: operadores de asignación, operadores aritméticos, operadores lógicos, operadores de comparación, operadores de igualdad y desigualdad, etc. Antes de continuar, ¿Que es un operador? Un operador es un símbolo o función que determina el tipo de operación o relaciónentre, el o los, operandos de una expresión.

Tabla de Contenido:

1. Introducción a los Operadores


2. Operadores de Asignación 1. Ejemplos Operadores de Asignación 3. Operadores Aritméticos 1. Ejemplos Operadores Aritméticos 4. Operadores Lógicos 1. Ejemplos Operadores Lógicos 5. Operadores de Comparación 1. Ejemplos Operadores de Comparación 6. Operadores a nivel de Bit 1. Ejemplos Operadores a nivel de Bit 7. Operadores de Igualdad y Desigualdad 8. Conclusión 9. Referencias

Operadores de Asignación: Los operadores de asignación sirven para, como su nombre indica, asignar valores a variables. Entre estos operadores, también encontramos los operadores de aumento o decremento de valores. Operador

Descripción

=

Operador de Asignación. También se usa para asignar valores por d funciones.

+=

Operador de asignación con incremento o suma de operandos. Tam concatenar strings, aunque como vimos, lo mejor es usar plantillas

-=

Operador de asignación con decremento o resta de operandos.

*=

Operador de asignación cuyo resultado es la multiplicación de oper


Operador

Descripción

/=

Operador de asignación cuyo resultado es la división de operandos.

%=

Operador de asignación cuyo resultado es el resto de la división de

Ejemplos Operadores de Asignación: Para ver como funcionan los operadores de Asignación, vamos a poner un par de ejemplos:

Operador de asignación: += // Ejemplos de asignación += var x: Int = 2 x += x // es igual a: x = x + x. Resultado: 4. var x: Int = 1 x += x // es igual a: x = x + x. Resultado: 2. // Ejemplos de asignación += con Strings. var x: String = "Hola" x += " Mundo" // es igual a: x = x + " Mundo". Resultado: "Hola M undo".

Operador de asignación: -= // Ejemplos de asignación -= var x: Int = 1 x -= x // es igual a: x = x - x. Resultado: 0. var x: Int = 4 x -= 2 // es igual a: x = x - 2. Resultado: 2.

Operador de asignación: *= // Ejemplos de asignación *= var x: Int = 1 x *= 5 // es igual a: x = x * 5. Resultado: 5. var x: Int = 4 x *= x // es igual a: x = x * x, o, x elevado a 2. Resultado: 16.


Operador de asignación: /= // Ejemplos de asignación /= var x: Int = 4 x /= 2 // es igual a: x = x / 2. Resultado: 2. var x: Int = 16 x /= x // es igual a: x = x / x. Resultado: 1.

Operador de asignación: %= // Ejemplos de asignación %= var x: Int = 4 x %= 2 // es igual a: x = x % 2. Resultado: 0. var x: Float = 4.5F x %= 2 // es igual a: x = x % 2. Resultado: 0.5

Operadores Aritméticos: Los operadores aritméticos sirven para realizar operaciones matemáticas entre operandos numéricos. El operador aritmético +, como vimos en el el articulo sobre tipos de variables, cadenas de texto (string), también sirve para concatenar strings, aunque es preferible utilizar las plantillas de strings. Operador

Descripción

+

Operador Aritmético + Suma de operandos o signo positivo. Tambi concatenación en Strings.

-

Operador Aritmético - Resta de operandos o signo negativo.

*

Operador Aritmético * Multiplicación de operandos. También se us parametro vararg.

/

Operador Aritmético / División de operandos.

%

Operador Aritmético % Resto de la división de operandos.


Ejemplos Operadores Aritméticos: Vamos a incluir unos ejemplos de uso:

Operador aritmético: + // Ejemplo de suma entre 2 enteros. var x: Int = 4 var y: Int = 6 var suma: Int suma = x + y // Resultado: 10. // Ejemplo de concatenación de dos String: var cadena1: String = "Hola" var cadena2: String = "Mundo" var concat: String concat = cadena1 + " " + cadena2 // Resultado: "Hola Mundo".

Operador aritmético: // Ejemplo de resta entre 2 enteros. var x: Int = 4 var y: Int = 6 var resta: Int resta = x - y // Resultado: -2.

Operador aritmético: * // Ejemplo de multiplicación entre 2 enteros. var x: Int = 4 var y: Int = 6 var multiplicacion: Int multiplicacion = x * y // Resultado: 24.

Operador aritmético: / // Ejemplo de división entre 2 enteros. var x: Int = 4 var y: Int = 6 var division: Int division = x / y // Resultado: 0. La variable division es entero, por eso no recoge los decimales. // Ejemplo de división entre 2 Floats. var x: Float = 4F var y: Float = 6F var division: Float division = x / y // Resultado: 0.6666667 Ahora si vemos el result ado con los decimales.


Operador aritmético: % // Ejemplo de resto de la división entre 2 enteros. var x: Int = 4 var y: Int = 6 var resto: Int resto = x % y // Resultado: 4.

Operadores Lógicos: Existen 3 operadores lógicos en Kotlin:

Operador de conjunción && , traducido como y. También se puede usar la palabra clave and .  Operador de disyunción || , traducido como o. También se puede usar la palabra clave or .  Operador de negación ! , traducido como no. 

Ejemplos Operadores Lógicos: Veamos unos ejemplos completos, con todas las posibilidades de uso, de estos operadores lógicos:

Operador de Conjunción: && // Ejemplo de conjunciones &&: var x: Boolean = false var y: Boolean = false var conjuncion: Boolean conjuncion = x && y // Resultado: false var x: Boolean = false var y: Boolean = true var conjuncion: Boolean conjuncion = x && y // Resultado: false var x: Boolean = true var y: Boolean = false var conjuncion: Boolean conjuncion = x && y // Resultado: fa lse var x: Boolean = true var y: Boolean = true


var conjuncion: Boolean conjuncion = x && y // Resultado: true

Como podemos observar, en la conjunciรณn, se deben cumplir ( Deben ser verdaderos ) los dos operandos.

Operador de Disyunciรณn: || // Ejemplo de disyunciones ||: var x: Boolean = false var y: Boolean = false var disyuncion: Boolean disyuncion = x || y // Resultado: false var x: Boolean = false var y: Boolean = true var disyuncion: Boolean disyuncion = x || y // Resultado: true var x: Boolean = true var y: Boolean = false var disyuncion: Boolean disyuncion = x || y // Resultado: true var x: Boolean = true var y: Boolean = true var disyuncion: Boolean disyuncion = x || y // Resultado: true

En las disyunciones, a diferencia de las conjunciones, con que se cumpla un operando nos resulta verdadero.

Operador negaciรณn: ! // Ejemplo de negaciรณn !: var x: Boolean = false var negacion: Boolean negacion = !x // Resultado: true var x: Boolean = true var negacion: Boolean negacion = !x // Resultado: false

Con la negaciรณn, invertimos el valor booleano de una variable, como se puede ver en los ejemplos.


Operadores de Comparación: Los operadores de comparación nos sirven para comparar 2 operandos. En kotlin, todas las comparaciones se traducen en llamadas al método compareTo() , en los tipos no primitivos, el cual retorna un entero Int . Expresión

Traducido a

Descripci

A<B

A.compareTo(B) < 0

Operador de comparación A menor

A>B

A.compareTo(B) > 0

Operador de comparación A mayor

A <= B

A.compareTo(B) <= 0

Operador de comparación A menor

A >= B

A.compareTo(B) >= 0

Operador de comparación A mayor

Para las variables de tipo Float o Double , cuando son tipificadas como tal, se pueden realizar comparaciones con rangos. A continuación, mostramos las expresiones de comparación de rangos para las variables de tipo Float o Double : Expresión

Descripción

X in A..B

Operador de comparación de rango, donde X está entre A (inicio d rango).

X !in A..B

Operador de comparación de rango, donde X NO está entre A (inic rango).

Ejemplos Operadores de Comparación: Operador de comparación: < // Ejemplo operador de comparación <


var x: Int = 4 var y: Int = 6 var comparacion: Boolean comparacion = x < y // Calculado: 4 < 6, resultado: true. var x: Float = 4.1F var y: Float = 4F var comparacion: Boolean comparacion = x < y // Calculado: 4.1 < 4, resultado: false. var x: Char = 'A' var y: Char = 'a' var comparacion: Boolean comparacion = x < y // Calculado: ('A' = 65) < ('a' = 97), result ado: true. // El ejemplo anterior se podría tipificar mejor, del siguiente m odo: var x: Int = 'A'.toInt() var y: Int = 'a'.toInt() var comparacion: Boolean comparacion = x < y // Calculado: 65 < 97, resultado: true.

Operador de comparación: > // Ejemplo operador de comparación > var x: Int = 4 var y: Int = 6 var comparacion: Boolean comparacion = x > y // Calculado: 4 > 6, resultado: false. var x: Float = 4.1F var y: Float = 4F var comparacion: Boolean comparacion = x > y // Calculado: 4.1 > 4, resultado: true. var x: Char = 'A' var y: Char = 'a' var comparacion: Boolean comparacion = x > y // Calculado: ('A' = 65) > (' a' = 97), result ado: false. // El ejemplo anterior se podría tipificar mejor, del siguiente m odo: var x: Int = 'A'.toInt() var y: Int = 'a'.toInt() var comparacion: Boolean comparacion = x > y // Calculado: 65 > 97, resultado: false.

Operador de comparación: <= // Ejemplo operador de comparación &lt=; var x: Int = 4 var y: Int = 6 var comparacion: Boolean


comparacion = x <= y // Calculado: 4 <= 6, resultado: true. var x: Float = 4.1F var y: Float = 4F var comparacion: Boolean comparacion = x <= y // Calculado: 4.1 <= 4, resultado: false. var x: Char = 'A' var y: Char = 'a' var comparacion: Boolean comparacion = x <= y // Calculado: ('A' = 65) <= ('a' = 97), resu ltado: true. // El ejemplo anterior se podría tipificar mejor, del siguiente m odo: var x: Int = 'A'.toInt() var y: Int = 'a'.toInt() var comparacion: Boolean comparacion = x <= y // Calculado: 65 <= 97, resultado: true.

Operador de comparación: >= // Ejemplo operador de comparación >= var x: Int = 4 var y: Int = 6 var comparacion: Boolean comparacion = x >= y // Calculado: 4 >= 6, resultado: false. var x: Float = 4.1F var y: Float = 4F var comparacion: Boolean comparacion = x >= y // Calculado: 4.1 >= 4, resultado: true. var x: Char = 'A' var y: Char = 'a' var comparacion: Boolean comparacion = x >= y // Calculado: ('A' = 65) >= ('a' = 97), resu ltado: false. // El ejemplo anterior se podría tipificar mejor, del siguiente m odo: var x: Int = 'A'.toInt() var y: Int = 'a'.toInt() var comparacion: Boolean comparacion = x >= y // Calcula do: 65 >= 97, resultado: false.

Operadores a nivel de Bit: Kotlin soporta operaciones a nivel de Bit, sobre variables tipo Int y Long únicamente. Sobre estos operadores, no existen caracteres especiales, pero si funciones que pueden ser invocadas entre dos operandos.


A continuación, veremos la tabla con estos operadores y una descripción de su funcionamiento: Función

Descripción

shl(bits)

Función de operador a nivel de Bits shl, desplazamiento hacia la izq signo (+ positivo o - negativo).

shr(bits)

Función de operador a nivel de Bits shr, desplazamiento hacia la de signo (+ positivo o - negativo).

ushr(bits)

Función de operador a nivel de Bits ushr, desplazamiento hacia la d signo (+ positivo o - negativo).

and(bits) Función de operador a nivel de Bits and (y). or(bits) Función de operador a nivel de Bits or (o). xor(bits) Función de operador a nivel de Bits xor (o exclusivo). inv()

Función de operador a nivel de Bits inv (inversión).

Ejemplos Operadores a nivel de Bit: Vamos a ver unos ejemplos de uso para estas fu nciones que, nos hacen de operadores a nivel de bit:

Operador a nivel de Bit: shl(bits) // Ejemplo "operadores" a nivel de Bit shl(bits): var x: Int = 2 var desplazamiento: Int = 4 var resultado: Int resultado = x shl desplazamiento // calculado: 2 shl 4, resultado : 32 /* calculo, paso a paso, del resultado: * 2 en binario: 10 * Bits de Desplazamiento: 4 (desplazamiento de x hacia la izquie rda, rellenando con 4 ceros delante.).


* resultado en binario de desplazar 2, 4 posiciones a la izquier da: 100000 * resultado en decimal: 32 */

Operador a nivel de Bit: shr(bits) // Ejemplo "operadores" a nivel de Bit shr(bits) var x: Int = -32 var desplazamiento: Int = 2 var resultado: Int resultado = x shr desplazamiento // calculado: -32 shr 2, resulta do: -8 /* calculo, paso a paso, del resultado: * -32 en binario: 100000, con el bit de signo en 1 (negativo), e l bit de signo positivo es 0. * Bits de Desplazamiento: 2 (desplazamiento de x hacia la derech a, rellenando con 2 ceros detrás). * resultado en binario de desplazar -32, 2 posiciones a la izqui erda: 001000 (quitamos los 2 últimos dígitos de la derecha). * resultado en decimal: -8 */

Operador a nivel de Bit: ushr(bits) // Ejemplo "operadores" a nivel de Bit ushr(bits) var x: Int = 30 var desplazamiento: Int = 2 var resultado: Int resultado = x ushr desplazamiento // calculado: 30 ushr 2, result ado: 7 /* calculo, paso a paso, del resultado: * 30 en binario: 11110 * Bits de Desplazamiento: 2 (desplazamiento de x hacia la derech a, rellenando con 2 ceros detrás). * resultado en binario de desplazar 30, 2 posiciones a la izquie rda: 00111 (quitamos los 2 últimos dígitos de la derecha). * resultado en decimal: 7 */

Operador a nivel de Bit: and(bits) // Ejemplo "operadores" a nivel de Bit a nd(bits) var x: Int = 5 var y: Int = 3 var resultado: Int resultado = x and y // calculado: 5 y 3, resultado: 1.

Vamos a explicar en detalle como funciona el operador and(bits) :


El operador and bit a bit, toma dos números enteros y realiza la operación and lógica en cada par correspondiente de bits. El resultado en cada posición es 1 si el bit correspondiente de los dos operandos es 1, y 0 de lo contrario. Os pongo una tabla, con el calculo anterior x = 5 e y = 3: X

Y

Resultado AND

1

0

0

0

1

0

1

1

1

Como podemos ver, el resultado de operar AND entre 5 y 3 en binario es 001, que se traduce en decimal como 1.

Operador a nivel de Bit: or(bits) // Ejemplo "operadores" a nivel de Bit or(bits) var x: Int = 5 var y: Int = 3 var resultado: Int resultado = x or y // calculado: 5 y 3, resultado: 7.

Vamos a explicar en detalle como funciona el operador or(bits) : Una operación or bit a bit, toma dos números enteros y realiza la operación or inclusivo en cada par correspondiente de bits. El resultado en cada posición es 1 si el bit correspondiente de cualquiera de los dos operandos es 1, y 0 si ambos bits son 0 . Os pongo una tabla, con el calculo anterior x = 5 e y = 3:


X

Y

Resultado OR

1

0

1

0

1

1

1

1

1

Como podemos ver, el resultado de operar OR entre 5 y 3 en binario es 111, que se traduce en decimal como 7.

Operador a nivel de Bit: xor(bits) // Ejemplo "operadores" a nivel de Bit xor(bits) var x: Int = 5 var y: Int = 3 var resultado: Int resultado = x xor y // calculado: 5 y 3, resultado: 6.

Vamos a explicar en detalle como funciona el operador xor(bits) : Una operación xor bit a bit, toma dos números enteros y realiza la operación or exclusivo en cada par correspondiente de bits. El resultado en cada posición es 1 si el par de bits son diferentes y cero si el par de bits son iguales. Os pongo una tabla, con el calculo anterior x = 5 e y = 3: X

Y

Resultado XOR

1

0

1

0

1

1

1

1

0


Como podemos ver, el resultado de operar XOR entre 5 y 3 en binario es 110, que se traduce en decimal como 6.

Operador a nivel de Bit: inv() // Ejemplo "operadores" a nivel de Bit inv() var x: Int = -6 var resultado: Int resultado = (x.inv() + 1) // calculado: inverso de -6 + 1, result ado: 6 var x: Int = 6 var resultado: Int resultado = (x.inv() + 1) // calculado: inverso de 6 + 1, resulta do: -6

* En la función de invertir bit a bit, no estoy 100% seguro de como opera. Pero añadiendo ese + 1, obtendremos el mismo número introducido con el signo (positivo o negativo) invertido.

Operadores de Igualdad y Desigualdad: Estos operadores de igualdad y desigualdad, nos permite comprobar si los operandos son equivalentes (para variables no primitivas, se traduce en la llamada al método equals() ). Los operadores de igualdad y desigualdad son los siguientes: Expresión

Traducción:

x == y

x?.equals(y) ?: (y === null)

Operador de ig

x != y

!(x?.equals(y) ?: (y === null))

Operador de d

La operación == se traduce en una expresión compleja que, también cubre los valores nulos null . null == null es siempre verdadero ( true ) y x == null para una variable x que no es


nula, el resultado será siempre falso ( false ) y no invocará la función x.equals() . También podemos realizar operaciones de igualdad y desigualdad de referencias. La igualdad de referencias se representa con el operador === y la desigualdad de referencias se representa con el operador !== . La expresión x === y , resulta verdadero ( true ) si, y solo si, x e y apuntan al mismo objeto. Para valores que se representan en tiempo de ejecución como variables primitivas (por ejemplo, Int ), el operador === se traduce al operador == y para la desigualdad, !== pasa lo mismo, se traduce a != .

Conclusión: En este articulo, hemos revisado los operadores, y funciones que trabajan como operadores, disponibles en kotlin. Si a la hora de leer este artículo te surge alguna duda, falta algún operador, conoces la forma de operar de la función inv(), etc. puedes dejar un comentario. Si te ha gustado el articulo, compártelo con tus amigos y/o en tus redes sociales.


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.