mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-03-24 02:20:09 +08:00
commit
1615f88c65
@ -1,44 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (The Zen of Python: Why timing is everything)
|
||||
[#]: via: (https://opensource.com/article/19/12/zen-python-timeliness)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez)
|
||||
|
||||
The Zen of Python: Why timing is everything
|
||||
======
|
||||
This is part of a special series about the Zen of Python focusing on the
|
||||
15th and 16th principles: now vs. never.
|
||||
![Clock, pen, and notepad on a desk][1]
|
||||
|
||||
Python is always evolving. The Python community has an unending appetite for feature requests but also an unending bias toward the status quo. As Python gets more popular, changes to the language affect more people.
|
||||
|
||||
The exact timing for when a change happens is often hard, but the [Zen of Python][2] offers guidance.
|
||||
|
||||
### Now is better than never.
|
||||
|
||||
There is always the temptation to delay things until they are perfect. They will never be perfect, though. When they look "ready" enough, that is when it is time to take the plunge and put them out there. Ultimately, a change always happens at _some_ now: the only thing that delaying does is move it to a future person's "now."
|
||||
|
||||
### Although never is often better than _right now_.
|
||||
|
||||
This, however, does not mean things should be rushed. Decide the criteria for release in terms of testing, documentation, user feedback, and so on. "Right now," as in before the change is ready, is not a good time.
|
||||
|
||||
This is a good lesson not just for popular languages like Python, but also for your personal little open source project.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/zen-python-timeliness
|
||||
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshez
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/desk_clock_job_work.jpg?itok=Nj4fuhl6 (Clock, pen, and notepad on a desk)
|
||||
[2]: https://www.python.org/dev/peps/pep-0020/
|
@ -1,11 +1,11 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Why you need to drop ifconfig for ip)
|
||||
[#]: via: (https://opensource.com/article/21/1/ifconfig-ip-linux)
|
||||
[#]: author: (Rajan Bhardwaj https://opensource.com/users/rajabhar)
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "MjSeven"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
[#]: subject: "Why you need to drop ifconfig for ip"
|
||||
[#]: via: "https://opensource.com/article/21/1/ifconfig-ip-linux"
|
||||
[#]: author: "Rajan Bhardwaj https://opensource.com/users/rajabhar"
|
||||
|
||||
Why you need to drop ifconfig for ip
|
||||
======
|
||||
@ -209,6 +209,6 @@ via: https://opensource.com/article/21/1/ifconfig-ip-linux
|
||||
|
||||
[a]: https://opensource.com/users/rajabhar
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/gears_devops_learn_troubleshooting_lightbulb_tips_520.png?itok=HcN38NOk (Tips and gears turning)
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/gears_devops_learn_troubleshooting_lightbulb_tips_520.png?itok=HcN38NOk "Tips and gears turning"
|
||||
[2]: https://en.wikipedia.org/wiki/OSI_model
|
||||
[3]: https://en.wikipedia.org/wiki/Multicast
|
||||
|
@ -1,5 +1,5 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: translator: (amwps290)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
@ -1,80 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (4 tips for preventing notification fatigue)
|
||||
[#]: via: (https://opensource.com/article/21/1/alert-fatigue)
|
||||
[#]: author: (Kevin Sonney https://opensource.com/users/ksonney)
|
||||
|
||||
4 tips for preventing notification fatigue
|
||||
======
|
||||
Don't overwhelm yourself with alerts—set the ones that matter and let
|
||||
the others go. You'll feel better and be more productive.
|
||||
![Working on a team, busy worklife][1]
|
||||
|
||||
In prior years, this annual series covered individual apps. This year, we are looking at all-in-one solutions in addition to strategies to help in 2021. Welcome to day 18 of 21 Days of Productivity in 2021.
|
||||
|
||||
One thing I notice when I talk to people about productivity is that almost everyone is doing it to keep a clearer head. Instead of keeping all our appointments in our heads, we put them on a digital calendar that alerts us before the event. We have digital or physical notes so that we don't have to remember every little detail of something. We have to-do lists to remind us to do the things we need to do.
|
||||
|
||||
![Text box offering to send notifications][2]
|
||||
|
||||
NOPE (Kevin Sonney, [CC BY-SA 4.0][3])
|
||||
|
||||
So many applications, websites, and services want to alert us about every little thing that it is easy just to tune them all out. And if we don't do that, we start to suffer from *alert fatigue—*where we are on edge, just waiting for that next alert, and living in dread of it.
|
||||
|
||||
Alert fatigue is very common among people who are on-call for their jobs. It also happens to people who have *FOMO—*the Fear Of Missing Out—and so set alerts on every keyword, hashtag, or mention on social media of a thing they are interested in.
|
||||
|
||||
Setting up alerts that will get our attention but won't be ignored is tricky with all this going on. However, I do have some helpful hints, so that maybe the important alerts get past our own mental filters in this hectic world.
|
||||
|
||||
![Alert for a task][4]
|
||||
|
||||
I can ignore this, right? (Kevin Sonney, [CC BY-SA 4.0][3])
|
||||
|
||||
1. Figure out what works better for you: Visual alerts or audible alerts. I use a combination of visual pop-ups and noises, but that is what works for me. Some people need tactile alerts—things like phone or watch vibrations. Find the one that works for you.
|
||||
2. Assign unique tones or visuals to important alerts. I have a friend who has the loudest, most obnoxious ring tone for work pages. It is _designed_ to get his attention and make him look at the alert. I have a light on my monitor that blinks red when I get an alert for work when I am on-call, as well as notifications sent to my phone.
|
||||
3. Turn off the alerts that don't actually matter. Social networks, websites, and apps want your attention. They don't care if you miss a meeting, are late to an appointment, or stay up until 4 am doom-scrolling. Turn off the ones that aren't important so the ones that are can be seen.
|
||||
4. Change things up every so often. What worked last month may not work next month. We adapt, we get used to things, and then we tune them out. If something isn't working, try something different! It can't hurt, and even if it doesn't work out, maybe you'll learn something new.
|
||||
|
||||
|
||||
|
||||
![Blue alert indicators light][5]
|
||||
|
||||
Blue is good. Red is bad. (Kevin Sonney, [CC BY-SA 4.0][3])
|
||||
|
||||
### Open source and choice
|
||||
|
||||
A good application provides lots of choices for notifications. One of my favorites is the Etar calendar app for Android. [Etar is available from the open source F-droid repository][6].
|
||||
|
||||
Etar, like many open source applications, provides you with all the options, especially for notification settings.
|
||||
|
||||
![Etar][7]
|
||||
|
||||
(Seth Kenlon, [CC BY-SA 4.0][3])
|
||||
|
||||
With Etar, you can activate or deactivate pop-up notifications, set snooze times, snooze delay, whether to remind you about declined events, and so on. Combined with an intentional scheduling strategy, you can change the course of your day by taking control of how often your digital assistance nudges you about things you need to do.
|
||||
|
||||
Reminders and alerts can be really useful, as long as we receive the ones that matter and pay attention to them. It can take some experimentation, but in the end, less noise is good, and it is easier to notice the alerts that truly need our attention.
|
||||
|
||||
Need to keep your schedule straight? Learn how to do it using open source with these free...
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/1/alert-fatigue
|
||||
|
||||
作者:[Kevin Sonney][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/ksonney
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/team_dev_email_chat_video_work_wfm_desk_520.png?itok=6YtME4Hj (Working on a team, busy worklife)
|
||||
[2]: https://opensource.com/sites/default/files/day18-image1.png
|
||||
[3]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[4]: https://opensource.com/sites/default/files/day18-image2.png
|
||||
[5]: https://opensource.com/sites/default/files/day18-image3.png
|
||||
[6]: https://f-droid.org/en/packages/ws.xsoh.etar/
|
||||
[7]: https://opensource.com/sites/default/files/etar.jpg (Etar)
|
@ -1,5 +1,5 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
@ -0,0 +1,240 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Convert audio files with this versatile Linux command)
|
||||
[#]: via: (https://opensource.com/article/20/2/linux-sox)
|
||||
[#]: author: (Klaatu https://opensource.com/users/klaatu)
|
||||
|
||||
Convert audio files with this versatile Linux command
|
||||
======
|
||||
SoX Sound Exchange can even add effects to your audio files.
|
||||
![HiFi vintage stereo][1]
|
||||
|
||||
I work with media, and when you work with any kind of media, you learn pretty quickly that standardization is a valuable tool. Just as you wouldn't try to add a fraction to a decimal without converting one or the other, I've learned that it's not ideal to combine media of differing formats. Most hobbyist-level applications make the conversion process invisible to the user as a convenience. Flexible software aimed at users needing control over the fine details of their assets, however, often leave it up to you to convert your media to your desired format in advance. I have a few favorite tools for conversion, and one of those is the so-called _Swiss army knife of sound_, [SoX][2].
|
||||
|
||||
### Installing
|
||||
|
||||
On Linux or BSD, you can install the **sox** command (and some helpful symlinks) from your software repository or ports tree.
|
||||
|
||||
You can also install SoX from its home on [Sourceforge.net][3]. It doesn't release often, but its codebase tends to be stable, so if you want the latest features (such as Opus support), it's easy and safe to build.
|
||||
|
||||
SoX provides primarily the **sox** command, but installation also creates a few useful symlinks: **play**, **rec**, and **soxi**.
|
||||
|
||||
### Getting information about files with SoX
|
||||
|
||||
SoX reads and rewrites audio data. Whether it stores the rewritten audio data is up to you. There are use cases in which you don't need to store the converted data, for instance, when you're sending the output directly to your speakers for playback. Before doing any conversion, however, it's usually a good idea to determine exactly what you're dealing with in the first place.
|
||||
|
||||
To gather information about an audio file, use the **soxi** command. This is a symlink to **sox --info**.
|
||||
|
||||
|
||||
```
|
||||
$ soxi countdown.mp3
|
||||
Input File : '/home/tux/countdown.mp3'
|
||||
Channels : 1
|
||||
Sample Rate : 44100
|
||||
Precision : 16-bit
|
||||
Duration : 00:00:11.21 = 494185 samples...
|
||||
File Size : 179k
|
||||
Bit Rate : 128k
|
||||
Sample Encoding: MPEG audio (layer I, II or III)
|
||||
```
|
||||
|
||||
This output gives you a good idea of what codec the audio file is encoded in, the file length, file size, sample rate, and the number of channels. Some of these you might _think_ you already know, but I never trust assumptions when media is brought to me by a client. Verify media attributes with **soxi**.
|
||||
|
||||
### Converting files
|
||||
|
||||
In this example, the audio of a game show countdown has been delivered as an MP3 file. While nearly all editing applications accept compressed audio, none of them actually edit the compressed data. Conversion is happening somewhere, whether it's a secret background task or a prompt for you to save a copy. I generally prefer to do the conversion myself, in advance. This way, I can control what format I'm using. I can do lots of media in batches overnight instead of wasting valuable production time waiting for an editing application to churn through them on demand.
|
||||
|
||||
The **sox** command is meant for converting audio files. There are a few stages in the **sox** pipeline:
|
||||
|
||||
* input
|
||||
* combine
|
||||
* effects
|
||||
* output
|
||||
|
||||
|
||||
|
||||
In command syntax, the effects step is, confusingly, written _last_. That means the pipeline is composed this way:
|
||||
|
||||
|
||||
```
|
||||
`input → combine → output → effects`
|
||||
```
|
||||
|
||||
### Encoding
|
||||
|
||||
The simplest conversion command involves only an input file and an output file. Here's the command to convert an MP3 file to a lossless FLAC file:
|
||||
|
||||
|
||||
```
|
||||
$ sox countdown.mp3 output.flac
|
||||
$ soxi output.flac
|
||||
|
||||
Input File : 'output.flac'
|
||||
Channels : 1
|
||||
Sample Rate : 44100
|
||||
Precision : 16-bit
|
||||
Duration : 00:00:11.18 = 493056 samples...
|
||||
File Size : 545k
|
||||
Bit Rate : 390k
|
||||
Sample Encoding: 16-bit FLAC
|
||||
Comment : 'Comment=Processed by SoX'
|
||||
```
|
||||
|
||||
#### Effects
|
||||
|
||||
The effects chain is specified at the end of a command. It can alter audio prior to sending the data to its final destination. For instance, sometimes audio that's too loud can cause problems during conversion:
|
||||
|
||||
|
||||
```
|
||||
$ sox bad.wav bad.ogg
|
||||
sox WARN sox: `bad.ogg' output clipped 126 samples; decrease volume?
|
||||
```
|
||||
|
||||
Applying a **gain** effect can often solve this problem:
|
||||
|
||||
|
||||
```
|
||||
`$ sox bad.wav bad.ogg gain -1`
|
||||
```
|
||||
|
||||
#### Fade
|
||||
|
||||
Another useful effect is **fade**. This effect lets you define the shape of a fade-in or fade-out, along with how many seconds you want the fade to span.
|
||||
|
||||
Here's an example of a six-second fade-in using an inverted parabola:
|
||||
|
||||
|
||||
```
|
||||
`$ sox intro.ogg intro.flac fade p 6`
|
||||
```
|
||||
|
||||
This applies a three-second fade-in to the head of the audio and a fade-out starting at the eight-second mark (the intro music is only 11 seconds, so the fade-out is also three-seconds in this case):
|
||||
|
||||
|
||||
```
|
||||
`$ sox intro.ogg intro.flac fade p 3 8`
|
||||
```
|
||||
|
||||
The different kinds of fades (sine, linear, inverted parabola, and so on), as well as the options **fade** offers (fade-in, fade-out), are listed in the **sox** man page.
|
||||
|
||||
#### Effect syntax
|
||||
|
||||
Each effect plugin has its own syntax, so refer to the man page for details on how to invoke each one.
|
||||
|
||||
Effects can be daisy-chained in one command, at least to the extent that you want to combine them. In other words, there's no syntax to apply a **flanger** effect only during a six-second fade-out. For something that precise, you need a graphical sound wave editor or a digital audio workstation such as [LMMS][4] or [Rosegarden][5]. However, if you just have effects that you want to apply once, you can list them together in the same command.
|
||||
|
||||
This command applies a -1 **gain** effect, a tempo **stretch** of 1.35, and a **fade-out**:
|
||||
|
||||
|
||||
```
|
||||
$ sox intro.ogg output.flac gain -1 stretch 1.35 fade p 0 6
|
||||
$ soxi output.flac
|
||||
|
||||
Input File : 'output.flac'
|
||||
Channels : 1
|
||||
Sample Rate : 44100
|
||||
Precision : 16-bit
|
||||
Duration : 00:00:15.10 = 665808 samples...
|
||||
File Size : 712k
|
||||
Bit Rate : 377k
|
||||
Sample Encoding: 16-bit FLAC
|
||||
Comment : 'Comment=Processed by SoX'
|
||||
```
|
||||
|
||||
### Combining audio
|
||||
|
||||
SoX can also combine audio files, either by concatenating them or by mixing them.
|
||||
|
||||
To join (or _concatenate_) files into one, provide more than one input file in your command:
|
||||
|
||||
|
||||
```
|
||||
`$ sox countdown.mp3 intro.ogg output.flac`
|
||||
```
|
||||
|
||||
In this example, **output.flac** now contains **countdown** audio, followed immediately by **intro** music.
|
||||
|
||||
If you want the two tracks to play over one another at the same time, though, you can use the **\--combine mix** option:
|
||||
|
||||
|
||||
```
|
||||
`$ sox --combine mix countdown.mp3 intro.ogg output.flac`
|
||||
```
|
||||
|
||||
Imagine, however, that the two input files differed in more than just their codecs. It's not uncommon for vocal tracks to be recorded in mono (one channel), but for music to be recorded in at least stereo (two channels). SoX won't default to a solution, so you have to standardize the format of the two files yourself first.
|
||||
|
||||
#### Altering audio files
|
||||
|
||||
Options related to the file name listed _after_ it. For instance, the **\--channels** option in this command applies _only_ to **input.wav** and NOT to **example.ogg** or **output.flac**:
|
||||
|
||||
|
||||
```
|
||||
`$ sox --channels 2 input.wav example.ogg output.flac`
|
||||
```
|
||||
|
||||
This means that the position of an option is very significant in SoX. Should you specify an option at the start of your command, you're essentially only overriding what SoX gleans from the input files on its own. Options placed immediately before the _output_ file, however, determine how SoX writes the audio data.
|
||||
|
||||
To solve the previous problem of incompatible channels, you can first standardize your inputs, and then mix:
|
||||
|
||||
|
||||
```
|
||||
$ sox countdown.mp3 --channels 2 countdown-stereo.flac gain -1
|
||||
$ soxi countdown-stereo.flac
|
||||
|
||||
Input File : 'countdown-stereo.flac'
|
||||
Channels : 2
|
||||
Sample Rate : 44100
|
||||
Precision : 16-bit
|
||||
Duration : 00:00:11.18 = 493056 samples...
|
||||
File Size : 545k
|
||||
Bit Rate : 390k
|
||||
Sample Encoding: 16-bit FLAC
|
||||
Comment : 'Comment=Processed by SoX'
|
||||
|
||||
$ sox --combine mix \
|
||||
countdown-stereo.flac \
|
||||
intro.ogg \
|
||||
output.flac
|
||||
```
|
||||
|
||||
SoX absolutely requires multiple commands for complex actions, so it's normal to create several temporary and intermediate files as needed.
|
||||
|
||||
### Multichannel audio
|
||||
|
||||
Not all audio is constrained to one or two channels, of course. If you want to combine several audio channels into one file, you can do that with SoX and the **\--combine merge** option:
|
||||
|
||||
|
||||
```
|
||||
$ sox --combine merge countdown.mp3 intro.ogg output.flac
|
||||
$ soxi output.flac
|
||||
|
||||
Input File : 'output.flac'
|
||||
Channels : 3
|
||||
[...]
|
||||
```
|
||||
|
||||
### Easy audio manipulation
|
||||
|
||||
It might seem strange to work with audio using no visual interface, and for some tasks, SoX definitely isn't the best tool. However, for many tasks, SoX provides an easy and lightweight toolkit. SoX is a simple command with powerful potential. With it, you can convert audio, manipulate channels and waveforms, and even generate your own sounds. This article has only provided a brief overview of its capabilities, so go read its man page or [online documentation][2] and then see what you can create.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/2/linux-sox
|
||||
|
||||
作者:[Klaatu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/klaatu
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/hi-fi-stereo-vintage.png?itok=KYY3YQwE (HiFi vintage stereo)
|
||||
[2]: http://sox.sourceforge.net/sox.html
|
||||
[3]: http://sox.sourceforge.net
|
||||
[4]: https://opensource.com/life/16/2/linux-multimedia-studio
|
||||
[5]: https://opensource.com/article/18/3/make-sweet-music-digital-audio-workstation-rosegarden
|
@ -0,0 +1,204 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How I build and expand application development and testing)
|
||||
[#]: via: (https://opensource.com/article/21/2/build-expand-software)
|
||||
[#]: author: (Alex Bunardzic https://opensource.com/users/alex-bunardzic)
|
||||
|
||||
How I build and expand application development and testing
|
||||
======
|
||||
Start development simply, by writing and testing your code with One
|
||||
element and then expand it out to Many.
|
||||
![Security monster][1]
|
||||
|
||||
In my [previous article][2], I explained why tackling coding problems all at once, as if they were hordes of zombies, is a mistake. I also explained the first **ZOMBIES** principle, **Zero**. In this article, I'll demonstrate the next two principles: **One** and **Many**.
|
||||
|
||||
**ZOMBIES** is an acronym that stands for:
|
||||
|
||||
**Z** – Zero
|
||||
**O** – One
|
||||
**M** – Many (or more complex)
|
||||
**B** – Boundary behaviors
|
||||
**I** – Interface definition
|
||||
**E** – Exercise exceptional behavior
|
||||
**S** – Simple scenarios, simple solutions
|
||||
|
||||
In the previous article, you implemented Zero, which provides the simplest possible path through your code. There is absolutely no conditional processing logic anywhere to be found. Now it's time for you to move into **O**ne.
|
||||
|
||||
Unlike with **Z**ero, which basically means nothing is added, or we have an empty case, nothing to take care of, **O**ne means we have a single case to take care of. That single case could be one item in the collection, or one visitor, or one event that demands special treatment.
|
||||
|
||||
With **M**any, we are now dealing with potentially more complicated cases. Two or more items in the collection, two or more events that demand special treatment, and so on.
|
||||
|
||||
### One in action
|
||||
|
||||
Build on the code from the previous article by adding something to your virtual shopping basket. First, write a fake test:
|
||||
|
||||
|
||||
```
|
||||
[Fact]
|
||||
public void Add1ItemBasketHas1Item() {
|
||||
var expectedNoOfItems = 1;
|
||||
var actualNoOfItems = 0;
|
||||
Assert.Equal(expectedNoOfItems, actualNoOfItems);
|
||||
}
|
||||
```
|
||||
|
||||
As expected, this test fails because you hard-coded an incorrect value:
|
||||
|
||||
|
||||
```
|
||||
Starting test execution, please wait...
|
||||
|
||||
A total of 1 test files matched the specified pattern.
|
||||
[xUnit.net 00:00:00.57] tests.UnitTest1.NewlyCreatedBasketHas0Items [FAIL]
|
||||
X tests.UnitTest1.NewlyCreatedBasketHas0Items [4ms]
|
||||
Error Message:
|
||||
Assert.Equal() Failure
|
||||
Expected: 0
|
||||
Actual: 1
|
||||
[...]
|
||||
```
|
||||
|
||||
Now is the time to think about how to stop faking it. You already created an implementation of a shopping basket (an `ArrayList` to hold items). But how do you implement an _item_?
|
||||
|
||||
Simplicity should always be your guiding principle, and not knowing much about the actual item, you could fake it a little by implementing it as another collection. What could that collection contain? Well, because you're mostly interested in calculating basket totals, the item collection should, at minimum, contain a price (in any currency, but for simplicity, use dollars).
|
||||
|
||||
A simple collection can hold an ID on an item (a pointer to the item, which may be kept elsewhere on the system) and the associated price of an item.
|
||||
|
||||
A good data structure that can easily capture this is a key/value structure. In C#, the first thing that comes to mind is `Hashtable`.
|
||||
|
||||
In the app code, add a new capability to the `IShoppingAPI` interface:
|
||||
|
||||
|
||||
```
|
||||
`int AddItem(Hashtable item);`
|
||||
```
|
||||
|
||||
This new capability accepts one item (an instance of a `Hashtable`) and returns the number of items found in the shopping basket.
|
||||
|
||||
In your tests, replace the hard-coded value with a call to the interface:
|
||||
|
||||
|
||||
```
|
||||
[Fact]
|
||||
public void Add1ItemBasketHas1Item() {
|
||||
var expectedNoOfItems = 1;
|
||||
Hashtable item = [new][3] Hashtable();
|
||||
var actualNoOfItems = shoppingAPI.AddItem(item);
|
||||
Assert.Equal(expectedNoOfItems, actualNoOfItems);
|
||||
}
|
||||
```
|
||||
|
||||
This code instantiates `Hashtable` and names it `item`, then invokes `AddItem(item)` on the shopping interface, which returns the actual number of items in the basket.
|
||||
|
||||
To implement it, turn to the `ShoppingAPI` class:
|
||||
|
||||
|
||||
```
|
||||
public int AddItem(Hashtable item) {
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
You are faking it again just to see the results of your tests (which are the first customers of your code). Should the test fail (as expected), replace the hard-coded values with actual code:
|
||||
|
||||
|
||||
```
|
||||
public int AddItem(Hashtable item) {
|
||||
basket.Add(item);
|
||||
return basket.Count;
|
||||
}
|
||||
```
|
||||
|
||||
In the working code, add an item to the basket, and then return the count of the items in the basket:
|
||||
|
||||
|
||||
```
|
||||
Test Run Successful.
|
||||
Total tests: 2
|
||||
Passed: 2
|
||||
Total time: 1.0633 Seconds
|
||||
```
|
||||
|
||||
So now you have two tests passing and have pretty much covered **Z** and **O**, the first two parts of **ZOMBIES**.
|
||||
|
||||
### A moment of reflection
|
||||
|
||||
If you look back at what you've done so far, you will notice that by focusing your attention on dealing with the simplest possible **Z**ero and **O**ne scenarios, you have managed to create an interface as well as define some processing logic boundaries! Isn't that awesome? You now have the most important abstractions partially implemented, and you know how to process cases where nothing is added and when one thing is added. And because you are building an e-commerce API, you certainly do not foresee placing any other boundaries that would limit your customers when shopping. Your virtual shopping basket is, for all intents and purposes, limitless.
|
||||
|
||||
Another important (although not necessarily immediately obvious) aspect of the stepwise refinement that **ZOMBIES** offers is a reluctance to leap head-first into the brambles of implementation. You may have noticed how sheepish this is about implementing anything. For starters, it's better to fake the implementation by hard-coding the values. Only after you see that the interface interacts with your test in a sensible way are you willing to roll up your sleeves and harden the implementation code.
|
||||
|
||||
But even then, you should always prefer simple, straightforward constructs. And strive to avoid conditional logic as much as you can.
|
||||
|
||||
### Many in action
|
||||
|
||||
Expand your application by defining your expectations when a customer adds two items to the basket. The first test is a fake. It expects 2, but force it to fail by hard-coding 0 items:
|
||||
|
||||
|
||||
```
|
||||
[Fact]
|
||||
public void Add2ItemsBasketHas2Items() {
|
||||
var expectedNoOfItems = 2;
|
||||
var actualNoOfItems = 0;
|
||||
Assert.Equal(expectedNoOfItems, actualNoOfItems);
|
||||
}
|
||||
```
|
||||
|
||||
When you run the test, two of them pass successfuy (the previous two, the **Z** and **O** tests), but as expected, the hard-coded test fails:
|
||||
|
||||
|
||||
```
|
||||
A total of 1 test files matched the specified pattern.
|
||||
[xUnit.net 00:00:00.57] tests.UnitTest1.Add2ItemsBasketHas2Items [FAIL]
|
||||
X tests.UnitTest1.Add2ItemsBasketHas2Items [2ms]
|
||||
Error Message:
|
||||
Assert.Equal() Failure
|
||||
Expected: 2
|
||||
Actual: 0
|
||||
|
||||
Test Run Failed.
|
||||
Tatal tests: 3
|
||||
Passed: 2
|
||||
Failed: 1
|
||||
```
|
||||
|
||||
Replace the hard-coded values with the call to the app code:
|
||||
|
||||
|
||||
```
|
||||
[Fact]
|
||||
public void Add2ItemsBasketHas2Items() {
|
||||
var expectedNoOfItems = 2;
|
||||
Hashtable item = [new][3] Hashtable();
|
||||
shoppingAPI.AddItem(item);
|
||||
var actualNoOfItems = shoppingAPI.AddItem(item);
|
||||
Assert.Equal(expectedNoOfItems, actualNoOfItems);
|
||||
}
|
||||
```
|
||||
|
||||
In the test, you add two items (actually, you're adding the same item twice) and then compare the expected number of items to the number of items from the `shoppingAPI` instance after adding the item the second time.
|
||||
|
||||
All tests now pass!
|
||||
|
||||
### Stay tuned
|
||||
|
||||
You have now completed the first pass of the **ZOM** part of the equation. You did a pass on **Z**ero, on **O**ne, and on **M**any. In the next article, I'll take a look at **B** and **I**. Stay vigilant!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/2/build-expand-software
|
||||
|
||||
作者:[Alex Bunardzic][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alex-bunardzic
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/security_password_chaos_engineer_monster.png?itok=J31aRccu (Security monster)
|
||||
[2]: https://opensource.com/article/21/1/zombies-zero
|
||||
[3]: http://www.google.com/search?q=new+msdn.microsoft.com
|
@ -0,0 +1,155 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Paru – A New AUR Helper and Pacman Wrapper Based on Yay)
|
||||
[#]: via: (https://itsfoss.com/paru-aur-helper/)
|
||||
[#]: author: (Dimitrios Savvopoulos https://itsfoss.com/author/dimitrios/)
|
||||
|
||||
Paru – A New AUR Helper and Pacman Wrapper Based on Yay
|
||||
======
|
||||
|
||||
One of the [main reasons that a user chooses Arch Linux][1] or an [Arch based Linux distribution][2] is the [Arch User repository (AUR)][3].
|
||||
|
||||
Unfortunately, [pacman][4], the package manager of Arch, can’t access the AUR in a similar way to the official repositories. The packages in AUR are in the form of [PKGBUILD][5] and require a manual process to be built.
|
||||
|
||||
An AUR helper can automate this process. Without any doubt [yay][6] is one of the most popular and highly favoured AUR helper.
|
||||
|
||||
Recently [Morganamilo][7], one of the two developers of yay, [announced][8] that is stepping away from maintaining yay and starting his own AUR helper called [paru][9]. Paru is written in [Rust][10] compared to yay that is written in [Go][11] and its design is based on yay.
|
||||
|
||||
Please note that yay hasn’t reach the end of life and is still being actively maintained by [Jguer][12]. He also [commented][13] that paru may be suitable for users that looking for a feature rich AUR helper; thus I would recommend giving it a try.
|
||||
|
||||
### Installing Paru AUR helper
|
||||
|
||||
To install paru, open your terminal and type the following commands one by one.
|
||||
|
||||
```
|
||||
sudo pacman -S --needed base-devel
|
||||
git clone https://aur.archlinux.org/paru.git
|
||||
cd paru
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
Now that you have it installed, let’s see how to use it.
|
||||
|
||||
### Essential commands to use Paru AUR helper
|
||||
|
||||
In my opinion these are the most essential commands of paru. You can explore more on the official repository on [GitHub][9].
|
||||
|
||||
* **paru <userinput>** : Search and install <userinput>.
|
||||
* **paru —** : Alias for paru -Syu
|
||||
* **paru -Sua** : Upgrade AUR packages only
|
||||
* **paru -Qua** : Print available AUR updates
|
||||
* **paru -Gc <userinput>** : Print the AUR comments of <userinput>
|
||||
|
||||
|
||||
|
||||
### Using Paru AUR helper to its full extent
|
||||
|
||||
You can access the [changelog][14] of paru on GitHub for the full changelog history or you can see the changes from yay at the [first release][15].
|
||||
|
||||
#### Enable colour in Paru
|
||||
|
||||
To enable colour in paru, you have to enable it first in pacman. All the [configuration files][16] are in /etc directory. In this example, I [use Nano text editor][17] but, you may use any [terminal-based text editor][18] of your choice.
|
||||
|
||||
```
|
||||
sudo nano /etc/pacman.conf
|
||||
```
|
||||
|
||||
Once you open the pacman configuration file, uncomment the “Color” to enable this feature.
|
||||
|
||||
![][19]
|
||||
|
||||
#### **Flip search order**
|
||||
|
||||
The most relevant package according to your search term is normally displayed on the top of the search result. In paru, you can flip the search order to make your search easier.
|
||||
|
||||
Similar to the previous example, open the paru configuration file:
|
||||
|
||||
```
|
||||
sudo nano /etc/paru.conf
|
||||
```
|
||||
|
||||
Uncomment the “BottomUp” term and save the file.
|
||||
|
||||
![][20]
|
||||
|
||||
As you can see the order is flipped and the first package appears on the bottom.
|
||||
|
||||
![][21]
|
||||
|
||||
#### **Edit PKGBUILDs** (For advanced user)
|
||||
|
||||
If you are an experienced Linux user, you can edit AUR packages through paru. To do so, you need to enable the feature from the paru configuration file and set the file manager of your choice.
|
||||
|
||||
In this example I will use the default in the configuration file i.e. the vifm file manager. If you haven’t used it you may need to install it.
|
||||
|
||||
```
|
||||
sudo pacman -S vifm
|
||||
sudo nano /etc/paru.conf
|
||||
```
|
||||
|
||||
Open the configuration file and uncomment as shown below.
|
||||
|
||||
![][22]
|
||||
|
||||
Let’s go back to the [Google Calendar][23] AUR package and try to install it. You will be prompted to review the package. Type yes and click enter.
|
||||
|
||||
![][24]
|
||||
|
||||
Choose the PKGBUILD from the file manager and hit enter to view the package.
|
||||
|
||||
![][25]
|
||||
|
||||
Any change that you make will be permanent and the next time you upgrade the package, your changes will be merged with the upstream package.
|
||||
|
||||
![][26]
|
||||
|
||||
### Conclusion
|
||||
|
||||
Paru is another interesting addition to the [AUR helpers family][27] with a promising future. At this point I wouldn’t suggest replacing yay as it is still maintained but definitely give paru a try. You can have both of them installed to your system and come to your own conclusions.
|
||||
|
||||
To get the latest [Linux news][28], subscribe to our social media to be among the first to get them whilst they are fresh!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/paru-aur-helper/
|
||||
|
||||
作者:[Dimitrios Savvopoulos][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/dimitrios/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://itsfoss.com/why-arch-linux/
|
||||
[2]: https://itsfoss.com/arch-based-linux-distros/
|
||||
[3]: https://itsfoss.com/aur-arch-linux/
|
||||
[4]: https://itsfoss.com/pacman-command/
|
||||
[5]: https://wiki.archlinux.org/index.php/PKGBUILD
|
||||
[6]: https://news.itsfoss.com/qt-6-released/
|
||||
[7]: https://github.com/Morganamilo
|
||||
[8]: https://www.reddit.com/r/archlinux/comments/jjn1c1/paru_v100_and_stepping_away_from_yay/
|
||||
[9]: https://github.com/Morganamilo/paru
|
||||
[10]: https://www.rust-lang.org/
|
||||
[11]: https://golang.org/
|
||||
[12]: https://github.com/Jguer
|
||||
[13]: https://aur.archlinux.org/packages/yay/#pinned-788241
|
||||
[14]: https://github.com/Morganamilo/paru/releases
|
||||
[15]: https://github.com/Morganamilo/paru/releases/tag/v1.0.0
|
||||
[16]: https://linuxhandbook.com/linux-directory-structure/#-etc-configuration-files
|
||||
[17]: https://itsfoss.com/nano-editor-guide/
|
||||
[18]: https://itsfoss.com/command-line-text-editors-linux/
|
||||
[19]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2021/01/pacman.conf-color.png?resize=800%2C480&ssl=1
|
||||
[20]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2021/01/paru.conf-bottomup.png?resize=800%2C480&ssl=1
|
||||
[21]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2021/01/paru.conf-bottomup-2.png?resize=800%2C480&ssl=1
|
||||
[22]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2021/01/paru.conf-vifm.png?resize=732%2C439&ssl=1
|
||||
[23]: https://aur.archlinux.org/packages/gcalcli/
|
||||
[24]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2021/01/paru-proceed-for-review.png?resize=800%2C480&ssl=1
|
||||
[25]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2021/01/paru-proceed-for-review-2.png?resize=800%2C480&ssl=1
|
||||
[26]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2021/01/paru-proceed-for-review-3.png?resize=800%2C480&ssl=1
|
||||
[27]: https://itsfoss.com/best-aur-helpers/
|
||||
[28]: https://news.itsfoss.com/
|
@ -0,0 +1,45 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (The Zen of Python: Why timing is everything)
|
||||
[#]: via: (https://opensource.com/article/19/12/zen-python-timeliness)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez)
|
||||
|
||||
Python 之禅:为什么时间就是一切
|
||||
======
|
||||
|
||||
> 这是 Python 之禅特别系列的一部分,重点是第十五和第十六条原则:现在与永远。
|
||||
|
||||
!["桌子上的时钟、笔和记事本"][1]
|
||||
|
||||
Python 一直在不断发展。Python 社区对特性请求的渴求是无止境的,对现状也总是不满意的。随着 Python 越来越流行,这门语言的变化会影响到更多的人。
|
||||
|
||||
确定什么时候该进行变化往往很难,但 [Python 之禅][2] 给你提供了指导。
|
||||
|
||||
### <ruby>现在有总比永远没有好<rt>Now is better than never</rt></ruby>
|
||||
|
||||
总有一种诱惑,就是要等到事情完美才去做,虽然,它们永远没有完美的一天。当它们看起来已经“准备”得足够好了,那就大胆采取行动吧,去做出改变吧。无论如何,变化总是发生在*某个*现在:拖延的唯一作用就是把它移到未来的“现在”。
|
||||
|
||||
### <ruby>虽然将来总比现在好<rt>Although never is often better than right now</rt></ruby>
|
||||
|
||||
然而,这并不意味着应该急于求成。从测试、文档、用户反馈等方面决定发布的标准。在变化就绪之前的“现在”,并不是一个好时机。
|
||||
|
||||
这不仅对 Python 这样的流行语言是个很好的经验,对你个人的小开源项目也是如此。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/zen-python-timeliness
|
||||
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshez
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/desk_clock_job_work.jpg?itok=Nj4fuhl6 (Clock, pen, and notepad on a desk)
|
||||
[2]: https://www.python.org/dev/peps/pep-0020/
|
@ -0,0 +1,76 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (4 tips for preventing notification fatigue)
|
||||
[#]: via: (https://opensource.com/article/21/1/alert-fatigue)
|
||||
[#]: author: (Kevin Sonney https://opensource.com/users/ksonney)
|
||||
|
||||
防止通知疲劳的 4 个技巧
|
||||
======
|
||||
不要让提醒淹没自己:设置重要的提醒,让其他提醒消失。 你会感觉更好,工作效率更高。
|
||||
![Working on a team, busy worklife][1]
|
||||
|
||||
在前几年,这个年度系列涵盖了单个的应用。今年,我们除了关注 2021 年的策略外,还将关注一体化解决方案。欢迎来到 2021 年 21 天生产力的第十八天。
|
||||
|
||||
当我和人们谈论生产力时,我注意到一件事,那就是几乎每个人都是为了保持更清晰的头脑。我们不是把所有的约会都记在脑子里,而是把它们放在一个数字日历上,在事件发生前提醒我们。我们有数字或实体笔记,这样我们就不必记住某件事的每一个小细节。我们有待办事项清单,提醒我们去做该做的事情。
|
||||
|
||||
![Text box offering to send notifications][2]
|
||||
|
||||
NOPE(Kevin Sonney, [CC BY-SA 4.0][3])
|
||||
|
||||
如此多的应用、网站和服务想要提醒我们每一件小事,以至于我们很容易就把它们全部调出来。而且,如果我们不这样做,我们将开始遭受**提醒疲劳**的困扰,这时我们处于边缘的状态,只是等待下一个提醒,并生活在恐惧之中。
|
||||
|
||||
提醒疲劳在那些因工作而被随叫随到的人中非常常见。它也发生在那些 **FOMO** (错失恐惧症)的人身上,从而对每一个关键词、标签或在社交媒体上提到他们感兴趣的事情都会设置提醒。
|
||||
|
||||
此时,设置能引起我们的注意,但不会被忽略的提醒是件棘手的事情。不过,我确实有一些有用的提示,这样重要的提醒可能会在这个忙碌的世界中越过我们自己的心理过滤器。
|
||||
|
||||
![Alert for a task][4]
|
||||
|
||||
我可以忽略这个,对吧?(Kevin Sonney, [CC BY-SA 4.0][3])
|
||||
|
||||
1. 弄清楚什么更适合你:视觉提醒或声音提醒。我使用视觉弹出和声音的组合,但这对我是有效的。有些人需要触觉提醒。比如手机或手表的震动。找到适合你的那一种。
|
||||
2. 为重要的提醒指定独特的音调或视觉效果。我有一个朋友,他的工作页面有最响亮、最讨厌的铃声。这个_设计_是为了吸引他的注意力,让他看到提醒。我的显示器上有一盏灯,当我在待命时收到工作提醒时,它就会闪烁红灯,以及发送通知到我手机上。
|
||||
3. 关掉那些其实不重要的警报。社交网络、网站和应用都希望得到你的关注。他们不会在意你是否错过会议、约会迟到,或者熬夜到凌晨4点厄运滚动。关掉那些不重要的,让那些重要的可以被看到。
|
||||
4. 每隔一段时间就改变一下。上个月有效的东西,下个月可能就不行了。我们会适应、习惯一些东西,然后我们会忽略。如果有些东西不奏效,就换个东西试试吧!它不会伤害你,即使无法解决问题,也许你也会学到一些新知识。
|
||||
|
||||
|
||||
![Blue alert indicators light][5]
|
||||
|
||||
蓝色是没问题。红色是有问题。(Kevin Sonney, [CC BY-SA 4.0][3])
|
||||
|
||||
### 开源和选择
|
||||
|
||||
一个好的应用为通知提供了很多选择。我最喜欢的一个是 Android 的 Etar 日历应用。[Etar 可以从开源 F-droid 仓库中获得][6]。
|
||||
|
||||
Etar 和许多开源应用一样,为你提供了所有的选项,尤其是通知设置。
|
||||
|
||||
![Etar][7]
|
||||
|
||||
(Seth Kenlon, [CC BY-SA 4.0][3])
|
||||
|
||||
通过 Etar,你可以激活或停用弹出式通知,设置打盹时间、打盹延迟、是否提醒你已拒绝的事件等。结合有计划的日程安排策略,你可以通过控制数字助手对你需要做的事情进行提示的频率来改变你一天的进程。
|
||||
|
||||
提醒和警报真的很有用,只要我们收到重要的提醒并予以注意即可。这可能需要一些实验,但最终,少一些噪音是好事,而且更容易注意到真正需要我们注意的提醒。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/21/1/alert-fatigue
|
||||
|
||||
作者:[Kevin Sonney][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/ksonney
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/team_dev_email_chat_video_work_wfm_desk_520.png?itok=6YtME4Hj (Working on a team, busy worklife)
|
||||
[2]: https://opensource.com/sites/default/files/day18-image1.png
|
||||
[3]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
[4]: https://opensource.com/sites/default/files/day18-image2.png
|
||||
[5]: https://opensource.com/sites/default/files/day18-image3.png
|
||||
[6]: https://f-droid.org/en/packages/ws.xsoh.etar/
|
||||
[7]: https://opensource.com/sites/default/files/etar.jpg (Etar)
|
Loading…
Reference in New Issue
Block a user