Taogen's Blog

Stay hungry stay foolish.

Buy your blog website domain

You can buy a domain from the following domain registers:

Installing Hexo

Before installing Hexo, you need installing the following software

  • Node.js (Should be at least Node.js 10.13, recommends 12.0 or higher)
  • Git

Installing Hexo

npm install -g hexo-cli
hexo -v

Uninstalling Hexo

npm uninstall -g hexo-cli

Create your Hexo project

cd hexo-site
hexo init .
npm install
npm install hexo

Configuring Hexo

Configuring basic website information in _config.yml

Setting Description
title The title of your website
subtitle The subtitle of your website. (slogan)
description The description of your website
author Your name

Run Hexo

cd hexo-site
hexo s --debug

Visit http://localhost:4000

NexT Theme

Installation NexT

If you’re using Hexo 5.0 or later, the simplest way to install is through npm.

cd hexo-site
npm install hexo-theme-next

Upgrading NexT

cd hexo-site
npm install hexo-theme-next@latest

Enable NexT

Enable NexT in _config.yml

theme: next

Checking NexT

hexo clean
hexo s --debug

Visit http://localhost:4000

Settings

Copy default NexT configuration file to root path

cp node_modules/hexo-theme-next/_config.yml _config.next.yml

Enable cache in _config.next.yml

cache:
enable: true

Enable minify in _config.next.yml

minify: true

Set Gemini Scheme in _config.next.yml

#scheme: Muse
#scheme: Mist
#scheme: Pisces
scheme: Gemini

Enable icons and badges in _config.next.yml

menu_settings:
icons: true
badges: true

Disable motion to load web pages faster in _config.next.yml

motion:
enable: false

Update codeblock settings in _config.next.yml. Choose your favorite code block theme on NexT Highlight Theme Preview. Recommend highlight themes: stackoverflow-light, intellij-light.

codeblock:
theme:
light: intellij-light
dark: intellij-light
copy_button:
enable: true

Disable line number in _config.yml (Optional)

highlight:
line_number: false

prismjs:
line_number: false

Enable scroll percent in _config.next.yml

back2top:
scrollpercent: true

Update favicon in _config.next.yml

favicon:
small: /favicon_16x16.ico
medium: /favicon_32x32.ico
apple_touch_icon: /favicon.jpg
safari_pinned_tab: /favicon.jpg

Adding Page

Enable menu home, about, tags, categories, achives in _config.next.yml

menu:
home: / || fa fa-home
about: /about/ || fa fa-user
tags: /tags/ || fa fa-tags
categories: /categories/ || fa fa-th
archives: /archives/ || fa fa-archive

Home and Archives pages exist by default.

We need to add tags, categories, and about pages

hexo new page tags && hexo new page categories && hexo new page about

Setting page type

  • source/tags/index.md: type: tags
  • source/categories/index.md: type: categories
  • source/about/index.md: type: about

For example:

title: Tags
date: xxx
type: tags

Re-run your hexo project to see the new theme settings.

hexo s --debug

Add Search Service

Add the Local Search

Installation

npm install hexo-generator-searchdb

Configuring Hexo config file _config.yml. Add the following configurations to the _config.yml file

search:
path: search.xml
field: post
content: true
format: html

Configuring NexT config file _config.next.yml

local_search:
enable: true

Deploying your Hexo website

Push your Hexo project to GitHub

Create a new git repository on GitHub

Push your hexo project to GitHub

git init .
git add -A
git commit -m "Initialize Hexo project"
git remote add origin git@github.com:tagnja/{your-repository-name}.git
git branch -M main
git push -u origin main

Deploying Hexo website to Vercel

Add new project on Vercel

Import Git Repository

Deploy the project

Domain settings for your project on Vercel

Add your domain in Vercel -> hexo project -> Settings -> Domains

Add vercel dns to your domain register

ns1.vercel-dns.com
ns2.vercel-dns.com

Visit your domain in web browser

References

Create a spring boot project

You can go to Spring Initializr to create a spring boot project

Setting your basic configurations. The following is my settings

  • Project: Maven
  • Language: Java
  • Spring Boot: 2.7.9
  • Project Metadata:
    • Group: com.taogen
    • Aritifact: chatgpt-demo
    • Packaging: Jar
    • Java: 8

You can set your own configurations according to your local environment.

After setting the project configurations, we need to add the following dependencies:

  • Spring Web
  • Lombok

Then we click the GENERATE button to download our project file.

After downloading the generated project file, we need to decompress the file and open the directory in our IDE. I prefer to use IntelliJ IDEA as my IDE.

Call the ChatGPT API in spring boot

Configuring your Open AI key in application.properties

openai.apiKey=${YOUR_OPEN_AI_KEY}

Create the RestTemplateConfig.java to build the RestTemplate bean for API calls

@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}

Create a POJO ChatGpt3dot5Request.java for building ChatGPT API requests

@Data
public class ChatGpt3dot5Request {
public static final String ENDPOINT = "https://api.openai.com/v1/chat/completions";

public static final String STREAM_MESSAGE_PREFIX = "data: ";

private String model = "gpt-3.5-turbo";

private Boolean stream;
List<Message> messages = new ArrayList<>();

@Data
@AllArgsConstructor
static class Message {
private String role;
private String content;
}
}

Create the controller ChatBotController.java

@RequestMapping("/chatbot")
@RestController
public class ChatBotController {

@Autowired
public RestTemplate restTemplate;

@Value("${openai.apiKey}")
private String OPEN_API_KEY;

@PostMapping("/conversation")
public void conversation(@RequestBody String prompt, HttpServletResponse httpServletResponse) throws IOException {
httpServletResponse.setContentType("text/plain; charset=UTF-8");
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString(ChatGpt3dot5Request.ENDPOINT)
.queryParams(null);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.setBearerAuth(OPEN_API_KEY);
String json = getRequestBody(prompt);
HttpEntity<Object> requestEntity =
new HttpEntity<>(json, httpHeaders);
ResponseEntity<Resource> responseEntity = restTemplate.exchange(builder.build().toUriString(), HttpMethod.POST, requestEntity, org.springframework.core.io.Resource.class);
PrintWriter writer = httpServletResponse.getWriter();
BufferedReader bufferedReader;
try {
bufferedReader = new BufferedReader(new InputStreamReader(responseEntity.getBody().getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null && !(ChatGpt3dot5Request.STREAM_MESSAGE_PREFIX + "[DONE]").equals(line)) {
String message = getMessageFromLine(line, ChatGpt3dot5Request.STREAM_MESSAGE_PREFIX);
writer.write(message);
writer.flush();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static String getMessageFromLine(String line, String prefix) throws JsonProcessingException {
if (!StringUtils.hasLength(line.trim())) {
return "";
}
String messageJsonStr = line.substring(line.indexOf(prefix) + prefix.length());
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode objectNode = (ObjectNode) objectMapper.readTree(messageJsonStr);
JsonNode messageNode = objectNode.get("choices").get(0).get("delta").get("content");
if (messageNode != null) {
return messageNode.asText();
} else {
return "";
}
}

private String getRequestBody(String prompt) throws JsonProcessingException {
ChatGpt3dot5Request chatGpt3dot5Request = new ChatGpt3dot5Request();
chatGpt3dot5Request.setStream(true);
chatGpt3dot5Request.setMessages(Arrays.asList(
new ChatGpt3dot5Request.Message("user", prompt)));
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(chatGpt3dot5Request);
return json;
}
}

The ChatBotController sends an HTTP request to ChatGPT API and reads its response stream line by line. Each line of the response has a similar structure. We extract the chat content by removing the prefix data: , parsing the string to a JSON object, and retrieving the content value. This content is then written into the response of our chatbot API. The following is an example of the content of the ChatGPT API response.

Click to expand!
data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"role":"assistant"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"Hi"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" there"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"!"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" How"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" can"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" I"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" assist"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" you"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" today"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"?"},"index":0,"finish_reason":null}]}

data: {"id":"chatcmpl-6zET7QIiAHSVeqoBwPNncxtIL3L6O","object":"chat.completion.chunk","created":1680051645,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{},"index":0,"finish_reason":"stop"}]}

data: [DONE]

Call your spring boot application API

Send a “hello” prompt to ChatGPT API by your API

curl -d 'hello' -H 'Content-Type: application/json' -X POST http://localhost:8080/chatbot/conversation
Hi there! How can I assist you today?

Note: If ChatGPT is not available in your region, you will not be able to call the ChatGPT API and the request will time out. Your application may throw the following exception:

2023-03-16 14:40:22.680 ERROR 14704 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://api.openai.com/v1/engines/davinci-codex/completions": Connection timed out: connect; nested exception is java.net.ConnectException: Connection timed out: connect] with root cause
java.net.ConnectException: Connection timed out: connect

Java

1. Use logging. Never use system.out.print().

For long-running programs, you’ll need to look at the log files to check that the program is functioning correctly.

2. For the scheduled task thread or any thread other than the main thread, catch the entire code block exception.

Generally, the main thread has global exception handling, but other threads do not.

3. No empty catch blocks.

An empty catch block won’t give you any information about what exception occurred. Probably the exception doesn’t matter, but it’s still an exceptional case.

4. Separate business and functional code.

Businesses are changing and functions are shared and reusable.

5. Divide your code by domain rather than by functionality or class type.

Usually, changes in your application are related to a specific domain, you should focus on business domains when building your structures, not functionality or class type.

6. Don’t create unnecessary variables, objects in a loop block.

Be careful when creating objects in a loop block. Create objects before a loop block if possible. Don’t create a lot of unnecessary objects in a loop block. For example, java.text.SimpleDateFormat, org.apache.poi.xssf.usermodel.XSSFCellStyle objects.

7. Don’t use Java internal code. All classes in com.sun.*, sun.*, or jdk.*are notionally internal, and should not be used directly in your code. All the classes you can use are in the Java API documentation, such as java.*, javax.*, and org.*.

Data Access

1. Never execute SQL statements in a for loop.

It takes a lot of time. You should find a way to do it in bulk.

Java Web and Spring Boot

1. Scheduling tasks should run when a specific profile is activated.

If the project is deployed on multiple servers, each server will execute scheduled tasks. Even if it activates different profiles on different servers.

2. API URLs use domain names rather than IP addresses.

If you use IP-based API URLs in many projects, you need to change many projects whenever the IP address changes.

3. Use Environment Variables in spring boot configuration files.

Don’t expose your confidential information in source code.

1. Use a new git branch to develop new features. After finishing a new feature, switch to the main branch.

It’s much clearer when you’re doing multiple functions at the same time.

2. The committed code needs to push to the remote repository as soon as possible.

Code in a local environment can easily get lost. One time I deleted the entire project directory and my code was lost.

3. The test code is for testing only. Don’t use test code as your functional source code.

4. Use environment variables for configurations. Don’t hardcode configuration.

It’s safe and convenient.

5. Don’t keep uncommitted changes of code all the time. Either commit and push it to a remote branch or restore it.

First, uncommitted and unpushed changes are easily lost. Second, keep uncommitted changes makes new updates to the same file difficult. Third, It also messes up code commits.

If the changes are important and you won’t need them for a while, you can copy and save them to another place.

6. First, solve the problem. Then, write the code. Before writing code, you need to understand and organize the requirements and complete the database design, API design, and detailed design.

It makes development easier.

7. Anything that can be automated should be automated.

The problem “cannot resolve symbol” means IDEA hasn’t successfully indexed your project code and dependent libraries.

There are two common problems that cause the “cannot resolve symbol” problems:

  1. Maven dependency configuration problems.
  2. IDEA problems.

Maven Configuration Problems

Ensure Maven dependency resolve correctly

Your project’s Maven dependencies must be resolved correctly. Otherwise, IDEA can’t index project files successfully.

To check that Maven dependencies are resolved correctly, you can execute the following command:

mvn -Dmaven.test.skip=true clean package

If you can’t pass the above command, then there is something wrong with your Maven configuration. You can update your pom.xml and run the above command again.

After you passed the above command by updating your pom.xml, you need to reload the maven project to download missing dependencies. Right click the pom.xml in IDEA -> Maven -> Reload project. If the “cannot resolve symbol” errors are gone, the problem is solved.

If you have passed the above command and reloaded the project but there are still the “cannot resolve symbol” problems in IDEA, then it’s IDEA problems not Maven. You can go to the “Problems with IDEA” section.

Common problems and solutions with Maven

Problem 1: The dependency cannot be found

This means that the dependency cannot be found from the remote Maven repository.

Error Information:

Could not resolve dependencies for project {your_project}: Failed to collect dependencies at {groupId}:{artifactId}.jar:{version}

Solutions:

Check if the groupId, artifactId or version of the dependency is correct.

Check if there is an unofficial repository defined in the project’s pom.xml or ~/.m2/settings.xml, try using Maven central repository.

<repository>
<id>central</id>
<name>Maven Central Repository</name>
<url>https://repo1.maven.org/maven2</url>
</repository>

Problem 2: dependency conflict

This means that multiple versions of a dependency are added or indirectly added to the Maven project. A dependency can only exist in one version. The actual version used causes the referenced method or class to be unable to be found.

Error Information:

NoClassDefFoundError, ClassNotFoundException, or NoSuchMethodError

Solutions

Use the “Maven Helper” plugin to check if there is a dependency conflict. You can try excluding unwanted versions by right-clicking on the version in the Maven Helper plugin window.

If your Maven project has a parent project, you also need to check whether the version defined in the parent project is compatible with your current project.

More tips

If you pom.xml is correct, but you still can’t pass mvn clean package. You can try to force update dependencies:

# force update the dependencies
mvn clean package -U

Problems with IDEA

Try to re-index your project files

Close the project or exit IDEA, then delete the .idea folder, then reopen project or restart IDEA. When there is no .idea folder in your project root path, IDEA will re-index project files automatically.

In addition to the .idea folder, if your project is a git repository, it’s recommended to delete all file ignored by git.

# delete git ignored files
git clean -dfx

After you re-indexed the project (delete .idea), if the “cannot resolve symbol” errors are gone, the problem is solved.

If the “cannot resolve symbol” errors still exist, you can try rebuild project. In top menu bar, Build -> Rebuild project.

If you have re-indexed the project (delete .idea) and rebuilt the project but the “cannot resolve symbol” errors still exist, you can go to the next step “Invalidate IDEA cache”.

Invalidate IDEA cache

IDEA cache also affects IDEA index project files. If your Maven configurations are right, but there are still “cannot resolve symbol” problems. You can try to Invalidate Caches:

File -> Invalidate Caches -> checked “clear file system cache and Local History”, and then click “Invalidate and Restart”.

Re-enter IDEA and waiting for “Update indexes” to complete. After “Update indexes” is done. Then we need rebulid the project:

Build -> click “Rebuild Project”.

After invalidated IDEA cache, if the “cannot resolve symbol” errors are gone, the problem is solved. Otherwise, You can try to re-index your project files again.

In this post, I will introduce several ways to find HTML DOM elements in JavaScript.

querySelector methods

Document or Element methods

querySelectorAll(selectors)

  • Parameters: the selectors parameter is a valid CSS selector string.
  • Return Value: returns a static (not live) NodeList representing a list of the document’s elements that match the specified group of selectors, or an empty NodeList in case of no matches.

querySelector(selectors)

  • Parameters: the selectors parameter is a valid CSS selector string.
  • Return Value: return an Element object representing the first element in the document that matches the specified set of CSS selectors, or null is returned if there are no matches.

Find by id

document.querySelector("#test")

Find by HTML tag

document.querySelectorAll("p")

Find by class names

document.querySelectorAll("div.className1")
// or
document.querySelectorAll(".className1")

Find by attributes

// match elements contain a attribute
document.querySelectorAll("elementName[attrName]")
// match attribute value
container.querySelectorAll("elementName[attrName='attrValue']");
// attribute value start with
container.querySelectorAll("elementName[attrName^='attrValue']");
// attribute value end with
container.querySelectorAll("elementName[attrName$='attrValue']");
// attribute value contains
container.querySelectorAll("elementName[attrName*='attrValue']");

and

document.querySelectorAll("div.className1.className2");
// or
document.querySelectorAll(".className1.className2");
document.querySelectorAll("div[attr1='value1'][attr2='value2']");
// or
document.querySelectorAll("[attr1='value1'][attr2='value2']");

or

document.querySelectorAll("div.className1, div.className2");
// or
document.querySelectorAll(".className1, .className2");

Find descendants, children, siblings with tag names

// descendants
document.querySelectorAll("div span");
// the first span descendant
document.querySelectorAll("div span:nth-of-type(1)");
// children
document.querySelectorAll("div>span");
// the first child element
document.querySelectorAll("div :nth-child(1)")
// siblings
document.querySelectorAll("div~img");
// adjacent sibling
document.querySelectorAll("div+img");
  • nth-child(n) : Selects the nth child of a parent, regardless of the type of element.
  • nth-of-type(n) : Selects the nth child of a specific type within its parent.

For more information about CSS selectors, you can see the CSS selectors documentation.

Traversing match elements

const matchedElements = document.querySelectorAll("div");
matchedElements.forEach((item) => {
console.log(item.innerText)
});

Filter elements

const matchedElements = document.querySelectorAll("div");
// querySelectorAll returns a NodeList not an Array. You can convert it to an Array
Array.from(matchedElements).filter((item) => item.classList.contains('note'));
const matchedElements = document.querySelectorAll("div");
Array.prototype.filter.call(
matchedElements,
(item) => item.classList.contains('note'),
);

getElementsBy methods

Document or Element methods

getElementById(id)

  • Return value: An Element object describing the DOM element object matching the specified ID, or null if no matching element was found in the document.

getElementsByClassName(className)

  • Parameters: className a string representing the class name(s) to match; multiple class names are separated by whitespace.
  • Return value: A live HTMLCollection of found elements.

getElementsByName(nameAttrValue)

  • Parameters: nameAttrValue the value of the name attribute of the element(s) we are looking for.
  • Return value: A live NodeList collection, meaning it automatically updates as new elements with the same name are added to, or removed from, the document.

getElementsByTagName(elementName)

  • Parameters: elementName a string representing the name of the elements. The special string * represents all elements.
  • Return value: A live HTMLCollection of found elements.

Traverse elements

const testElements = document.getElementsByClassName('test');
for (let i = 0; i < testElements.length; i++) {
console.log(testElements[i].innerText)
}

Filter elements

const matchedElements = document.getElementsByTagName("div");
Array.from(matchedElements).filter((item) => item.classList.contains('yourClassName'));
const matchedElements = document.getElementsByTagName("div");
Array.prototype.filter.call(
matchedElements,
(item) => item.classList.contains('yourClassName'),
);

The closest method

closest(selector) looks for the nearest ancestor that matches the CSS-selector.

Summary

Method Searches by Call on an element Live
querySelector CSS-selector Yes -
querySelectorAll CSS-selector Yes -
getElementById id - -
getElementsByName name - Yes
getElementsByTagName tag or '*' Yes Yes
getElementsByClassName class Yes Yes

References

[1] Searching: getElement*, querySelector*

[2] Document.querySelector() - mdn

[3] Document.querySelectorAll() - mdn

[4] Document.getElementsByClassName() - mdn

[5] CSS selectors - mdn

In this post, I will introduce the basic concepts of Docker. You’ll learn what docker is, why and when to use it.

What is Docker

Docker is an open source project for building, shipping, and running programs. It is a command line program, a background process, and a set of remote services that take a logistical approach to solving common software problems and simplifying your experience installing, running, publishing, and removing software. It accomplishes this by using an operating system technology called containers.

Running the hello-world in a container

Before running the hello-world program in a container, you need install Docker on your computer. You can download and install a Docker Desktop from https://docs.docker.com/install/. If you want to use docker on a Linux cloud server, you can download and install a Docker Engine from https://docs.docker.com/engine/install/.

After Docker is up and running on your computer, you can enter the following command to run the hello-world program provided by Docker in a container:

docker run hello-world
# or
docker run library/hello-world

After execute the above command you can see the output of the hello-world program

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

After print the above text, the program exits, and container is marked as stopped. The running state of a container is directly tied to the state of a single running program inside the container. If a program is running, the container is running. If the program is stopped, the container is stopped. Restarting a container will run the program again.

In the second time to run a container, you can use docker start <container> to run an existing container directly instead of create a new similar container from its image again.

The process of the docker run command execution is:

The hello-world is called the image or repository name. You can think of the image name as the name of the program you want to install or run. The image is a collection of files and metadata. The metadata includes the specific program to execute and other relevant configuration details.

Docker Hub is a public registry provide by Docker Inc. It is a repository service and it is a cloud-based service where people push their Docker Container Images and also pull the Docker Container Images from the Docker Hub.

Container

Historically, UNIX-style operating systems have used the term jail to describe a modified runtime environment that limits the scope of resources that a jailed program can access. Jail features go back to 1979 and have been in evolution ever since. In 2005, with the release of Sun’s Solaris 10 and Solaris Containers, container has become the preferred term for such a runtime environment. The goal has expanded from limiting filesystem scope to isolating a process from all resources except where explicitly allowed.

Using containers has been a best practice for a long time. But manually building containers can be challenging and easy to do incorrectly. Docker uses existing container engines to provide consistent containers built according to best practices. This puts stronger security within reach for everyone.

Containers vs Virtual machines

Virtual machines

  • Every virtual machine has a whole operating system
  • Take a long time (often minutes) to create.
  • Require significant resource overhead.

Container

  • All Docker containers share an operating system.
  • Docker containers don’t use any hardware virtualization. Programs running inside Docker containers interface directly with the host’s Linux kernel.
  • Many programs can run in isolation without running redundant operating systems or suffering the delay of full boot sequences.

Running software in containers for isolation

Each container is running as a child process of the Docker engine, wrapped with a container, and the delegate process is running in its own memory subspace of the user space. Programs running inside a container can access only their own memory and resources as scoped by the container.

Shipping containers

Docker use images to shipping containers. A Docker image is a bundled snapshot of all the files that should be available to a program running inside a container. You can create as many containers from an image as you want. Images are the shippable units in the Docker ecosystem.

Docker provides a set of infrastructure components that simplify distributing Docker images. These components are registries and indexes. You can use publicly available infrastructure provided by Docker Inc., other hosting companies, or your own registries and indexes. You can store and search images from a registry.

Why Use Docker

Docker makes it easy and simple to use the container and isolation features provided by operating systems.

Why use the container and isolation features

  • Dependency conflict.
  • Portability between operating systems. Docker runs natively on Linux and comes with a single virtual machine for macOS and Windows environments. You can run the same software on any system.
  • Protecting your computer. Docker prevents malicious program attacks through operating system resource access control.
  • Application removal. All program execution files and program-produced files are in a container. You can remove all of these files easily.

When to use Docker

Docker can run almost anywhere for any application. But currently Docker can run only applications that can run on a Linux operating system, or Windows applications on Windows Server. If you want to run a macOS or Windows native application on your desktop, you can’t yet do so with Docker.

References

[1] Jeffrey, Nickoloff and Stephen, Kuenzli. Docker in Action. 2nd ed., Manning Publications, 2019.

Pass data between a parent and a child component

Pass data from parent component to its child

Use props

Parent.js

<Child name={childName}></Child>

Child.js

<p>Receive from parent by props: {this.props.name}</p>

Pass data from child component to its parent

Pass function from parent component to its child.

The Child component call the function with parameters.

A complete example

Click to expand!

Parent.js

import { PureComponent } from 'react';
import { Input } from 'antd';

class Parent extends PureComponent {

constructor(props) {
super(props);
this.state = ({
childName: "default child name",
name: "default parent name"
});

}

handleInputChange = (e) => {
const name = e.target.value;
this.setState({
childName: name
})
}

handleParentValueChange = (value) => {
this.setState({
name: value
})
}

render() {
return (
<div>
<h2>I'm the parent page</h2>
<p>Receive from child: {this.state.name}</p>
Enter data to pass to child: <Input onChange={this.handleInputChange}></Input>
<Child name={this.state.childName} onParentNameChange={this.handleParentValueChange}></Child>
</div>
)
}
}

export default Parent;

Child.js

import { PureComponent } from 'react';
import { Input } from 'antd';

class Child extends PureComponent {

constructor(props) {
super(props);
this.state = ({});
}

handleInputChange = (e) => {
const name = e.target.value;
this.props.onParentNameChange(name);
}

render() {
return (
<div style={{backgroundColor: "lightgray", padding: "10px 10px 10px 10px"}}>
<h2>I'm the child page</h2>
<p>Receive from parent by props: {this.props.name}</p>
Enter data to pass to parent: <Input onChange={this.handleInputChange}></Input>
</div>
)
}
}

export default Child;

Pass functions between a parent and a child component

Pass functions to child components

If you need to have access to the parent component in the handler, you also need to bind the function to the component instance (see below).

There are several ways to make sure functions have access to component attributes like this.props and this.state.

Bind in Constructor (ES2015)

class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}

Note: Make sure you aren’t calling the function when you pass it to the component

render() {
// Wrong: handleClick is called instead of passed as a reference!
// The function being called every time the component renders.
return <button onClick={this.handleClick()}>Click Me</button>
}

Class Properties (ES2022)

class Foo extends Component {
handleClick = () => {
console.log('Click happened');
};
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}

Note: Make sure you aren’t calling the function when you pass it to the component

render() {
// Wrong: handleClick is called instead of passed as a reference!
// The function being called every time the component renders.
return <button onClick={this.handleClick()}>Click Me</button>
}

Bind in Render

class Foo extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={this.handleClick.bind(this)}>Click Me</button>;
}
}

Pass a parameter to an event handler in parent component’s render method. Values passed by child components will be ignored.

<button onClick={this.handleClick.bind(this, id)} />

Note: Using Function.prototype.bind in render creates a new function each time the component renders, which may have performance implications.

Arrow Function in Render

class Foo extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={() => this.handleClick()}>Click Me</button>;
}
}

Pass a parameter to an event handler

<button onClick={() => this.handleClick(id)} />

Note: Using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison.

Call child functions in a parent component


refs

Previously, refs were only supported for Class-based components. With the advent of React Hooks, that’s no longer the case.

Modern React with Hooks (v16.8+)

Hook parent and hook child (Functional Component Solution)

const { forwardRef, useRef, useImperativeHandle } = React;

const Parent = () => {
// In order to gain access to the child component instance,
// you need to assign it to a `ref`, so we call `useRef()` to get one
const childRef = useRef();

return (
<div>
<Child ref={childRef} />
<button onClick={() => childRef.current.getAlert()}>Click</button>
</div>
);
};

// We need to wrap component in `forwardRef` in order to gain
// access to the ref object that is assigned using the `ref` prop.
// This ref is passed as the second parameter to the function component.
const Child = forwardRef((props, ref) => {

// The component instance will be extended
// with whatever you return from the callback passed
// as the second argument
useImperativeHandle(ref, () => ({
getAlert() {
alert("getAlert from Child");
}
}));
return <h1>Hi</h1>;
});

Legacy API using Class Components (>= react@16.4)

Class parent and class child (Class Component Solution)

class Parent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}

render() {
return (<View>
<Child ref={this.myRef}/>
<Button title={'call me'}
onPress={() => this.myRef.current.childMethod()}/>
</View>)
}
}

class Child extends React.Component {

childMethod() {
console.log('call me')
}

render() {
return (<View><Text> I am a child</Text></View>)
}
}

Class component and Hook

Class parent and hook child

class Parent extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}

render() {
return (<View>
<Child ref={this.myRef}/>
<Button title={'call me'}
onPress={() => this.myRef.current.childMethod()}/>
</View>)
}
}

const Child = React.forwardRef((props, ref) => {

useImperativeHandle(ref, () => ({
childMethod() {
childMethod()
}
}))

function childMethod() {
console.log('call me')
}

return (<View><Text> I am a child</Text></View>)
})

Hook parent and class child

function Parent(props) {

const myRef = useRef()

return (<View>
<Child ref={myRef}/>
<Button title={'call me'}
onPress={() => myRef.current.childMethod()}/>
</View>)
}

class Child extends React.Component {

childMethod() {
console.log('call me')
}

render() {
return (<View><Text> I am a child</Text></View>)
}
}

useEffect

Parent

const [refresh, doRefresh] = useState(0);
<Button onClick={() => doRefresh(prev => prev + 1)} />
<Children refresh={refresh} />

Children

useEffect(() => {
performRefresh(); //children function of interest
}, [props.refresh]);

Others

class Parent extends Component {
render() {
return (
<div>
<Child setClick={click => this.clickChild = click}/>
<button onClick={() => this.clickChild()}>Click</button>
</div>
);
}
}

class Child extends Component {
constructor(props) {
super(props);
this.getAlert = this.getAlert.bind(this);
}
componentDidMount() {
this.props.setClick(this.getAlert);
}
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}

Share data between components with Redux

A basic example:

Click to expand!
import { createStore } from 'redux'

/**
* This is a reducer - a function that takes a current state value and an
* action object describing "what happened", and returns a new state value.
* A reducer's function signature is: (state, action) => newState
*
* The Redux state should contain only plain JS objects, arrays, and primitives.
* The root state value is usually an object. It's important that you should
* not mutate the state object, but return a new object if the state changes.
*
* You can use any conditional logic you want in a reducer. In this example,
* we use a switch statement, but it's not required.
*/
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}

// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
let store = createStore(counterReducer)

// You can use subscribe() to update the UI in response to state changes.
// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly.
// There may be additional use cases where it's helpful to subscribe as well.

store.subscribe(() => console.log(store.getState()))

// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}

Pass data to redirect component

React Router

Pass data with <Redirect>:

<Redirect to={{
pathname: '/nav',
state: { id: '123' }
}}
/>

Pass data with history.push():

history.push({
pathname: '/about',
search: '?the=search',
state: { some: 'state' }
})

Access data:

this.props.location.state.id

UmiJS

Pass data with query string

import { history, router } from 'umi';

history.push('/path?field=value&field2=value2')
// or
history.push({
pathname: '/path',
query: {
field: value
}
})
// or
router.push('/path?field=value&field2=value2')
// or
router.push({
pathname: '/path',
query: {
field: value
}
})

Access query string

import { useLocation } from 'umi';

const location = useLocation();
console.log(location.query)

URL query string

Pass data to components use URL query string: url?field=value&field2=value2

Get query string parameters

const params = new Proxy(new URLSearchParams(window.location.search), {
get: (searchParams, prop) => searchParams.get(prop),
});
// Get the value of "some_key" in eg "https://example.com/?some_key=some_value"
let value = params.some_key; // "some_value"

References

Design

Creating good architecture design.

Creating good database design.

Creating good API design.

Unit test

Write unit tests or test-drive development.

Keep tests clean.

Name

Creating good names

Don’t use literals.

Functions

Keep functions small and do one thing

Write functions as writing a story.

Code

Avoid repeated code. Extract repeated code into a common function or utility function.

Consider using better data structures and algorithms.

Classes

Keep small classes

Consider creating a better class hierarchy

Consider using a better design pattern

Error Handling

Consider possible exceptions for each line of code.

match and match_phrase do not work for partial searches. We can use wildcard, query_string or regexp to match a partial string.

For example, search gt3p in the content column.

wildcard

{
"query": {
"wildcard": {
"content" : "*gt3p*"
}
}
}

query_string

{
"query": {
"query_string": {
"default_field": "content",
"query": "*gt3p*"
}
}
}

regexp

{
"query": {
"regexp": {
"content": ".*gt3p.*"
}
}
}
0%