Sheldon  Grant

Sheldon Grant

1669698120

Best Tableau Tips and Tricks a Data Visualization Expert Must Know

Are you the one who builds outstanding dashboards for people in your company? If yes, then my friend you have crawled over to the right post. In this post, I’ve highlighted some of the amazing “Tableau tips and tricks” which you can apply right away and make your dashboards even more effective to stand out in the crowd.

You know what, you can create calendars within the tableau, or even animate your visualization. Check out this Tableau course online which covers each & every nuance of Tableau, and teaches how to create an effective dashboard.

idea - Tableau tips and tricks - Edureka

Be the one to stand out in the crowd – Joel Osteen

Here are some Tableau tips and tricks for you:

1. Creating Calendar in Tableau 

2. Switch Between Fields

3. Blend like a Pro!

4. Big Numbers

5. Animated Visualization

Tableau Tips and Tricks - Edureka

1. Tableau tips and tricks: Calendar in Tableau

This is a simple yet an out of the box trick which you can use to impress your colleagues. Let’s learn how to create Calendar in just 4 steps!

Step 1: Create a dataset for the year:

To start with, you need some data that has dates in it. For this blog, I simply went to Excel, entered 1/01/2018, and pulled down to 31/12/2018, saved the file in .xls format and connect the data-set to Tableau.

Creating Excel Data-set - Calendar - Tableau Tips and Tricks - Edureka

Step 2: Adding date to column shelf

Add ‘Order Date’ to column shelf and change it to (‘Month/Year’) Custom date format

Creating Calendar - Tableau Tips and Tricks - Edureka

Step 3: Adding Week and Weekday to Row and Column shelf

Change ‘Order Date’ to weekday and week number and add it to Column and Row shelf.

Creating Calendar Step 3- Tableau Tips and Tricks - Edureka

Step 4: Adding Filters and Marks to finally build the Calendar

Use filters and marks to display the monthly calendar

Creating Calendar in Tableau - Step 4 - Tableau Tips and Tricks - Edureka

Final Calendar: 

Well, this is the final calendar which you will get at the end. Just an FYI, you can also add other parameters like sales, or profit and get a calendar view of them.

Calendar - Tableau Tips and Tricks - Edureka

 

2. Tableau Tips and Tricks: Switch between Fields

Let’s learn another hack from tableau and learn how to use a parameter to give the end user the ability to switch through different metrics. 

Step 1: Create Parameter

Create a String Parameter ‘Measure Switch’. In order to give the ability to switch between different measures, let’s create a list of parameters. Under the list of values on the left side we have the actual value of the parameter and on the right, we have a name which the user clicking on the parameter will see. So let’s fill it out in the following manner.

Measure Switch - Tableau Tips and Tricks - Edureka

Calculated Field - Tableau Tips and Tricks - Edureka

Step 2: Create Calculated Field

Next steps come is creating a calculated field. Our calculated field will check the parameter, and based on the value it will pick a visualization and show the visualization. Here’s what it looks like:

Step 3: Create Visualization I

Done with creating calculated field? Let’s move on to create a visualization by adding ‘Region’ to rows and ‘Department’ to columns. Also, add the newly created calculated field ‘Switched Measures’ to the column. Finally, add the same calculated field to the colour and the text shelf.

Switching Measure - Tableau Tips and Tricks - Edureka

Step 4: Create Visualization II

1. On a new sheet, change the marks to ‘filled map’ and add “State” to details.

2. We’re going to eventually create a dual axis map, so add Latitude on the rows shelf.

3. On the marks shelf, add the parameter we created “Measure Switch” to the colours shelf.

4. Now click the map below that one and add the calculated field we made “Switched Measures” to the text and colour shelf.

5. Finally, right click on the second pill on the rows self and set to the dual axis. Here’s what it looks like:

Creating Visualization II - Tableau Tips and Tricks - Edureka

 

3. Tableau Tips and Tricks: Blend Like a Pro!

Data blending is a method for combining data coming from one data source with columns of data from another data source.

Usually, you can use joins to combine your data, but at times when factors like the type of data and its granularity is to be considered then in such cases, it’s better to use data blending.

For example, suppose you have a part of data stored in Salesforce and rest in an Excel workbook. Now since the data you want to combine is stored in two different databases, and the granularity of the data captured in each table is also different in the two data sources, so data blending is the best way to combine this data.

 

4. Tableau Tips and Tricks: Big Numbers

This is a quick but interesting trick to learn. Its a very simple trick but adds better aesthetics to your dashboard. Let me show you an example of the dashboard:

Big Numbers - Tableau Tips and Tricks - Edureka

Pretty interesting!! right? Let me show you, it’s a very easy trick.

Big Numbers - Tableau Tips and Tricks

 

5. Tableau Tips and Tricks: Animated Visualization

Yes! you heard it right, you can add animation to your visualization. Let’s visualize how the population of the world increased in past 200 years. Sounds cool right! This is where Tableau’s Page Shelf come into the picture. Let’s add the date to the page shelf and see how crude birth rate has reduced over year.

Animation with Page Shelf - Tableau Tips and Ticks - Edureka

I hope you enjoyed my blog and found some hidden features of Tableau. Just reading won’t make you a Tableau Jedi, go Ahead and start exploring Tableau. If you wanna learn about more such exciting features in Tableau, click below to enrol yourself and get certified in Edureka’s Tableau Training and Certification Program.

Original article source at: https://www.edureka.co/

#datavisualization #tips #tricks 

Best Tableau Tips and Tricks a Data Visualization Expert Must Know
Rupert  Beatty

Rupert Beatty

1666245660

A Collection Of Swift Tips & Tricks That I've Shared on Twitter

Swift tips & tricks ⚡️

One of the things I really love about Swift is how I keep finding interesting ways to use it in various situations, and when I do - I usually share them on Twitter. Here's a collection of all the tips & tricks that I've shared so far. Each entry has a link to the original tweet, if you want to respond with some feedback or question, which is always super welcome! 🚀

⚠️ This list is no longer being updated. For my latest Swift tips, checkout the "Tips" section on Swift by Sundell.

Also make sure to check out all of my other Swift content:

102 Making async tests faster and more stable

🚀 Here are some quick tips to make async tests faster & more stable:

  • 😴 Avoid sleep() - use expectations instead
  • ⏱ Use generous timeouts to avoid flakiness on CI
  • 🧐 Put all assertions at the end of each test, not inside closures
// BEFORE:

class MentionDetectorTests: XCTestCase {
    func testDetectingMention() {
        let detector = MentionDetector()
        let string = "This test was written by @johnsundell."

        detector.detectMentions(in: string) { mentions in
            XCTAssertEqual(mentions, ["johnsundell"])
        }
        
        sleep(2)
    }
}

// AFTER:

class MentionDetectorTests: XCTestCase {
    func testDetectingMention() {
        let detector = MentionDetector()
        let string = "This test was written by @johnsundell."

        var mentions: [String]?
        let expectation = self.expectation(description: #function)

        detector.detectMentions(in: string) {
            mentions = $0
            expectation.fulfill()
        }

        waitForExpectations(timeout: 10)
        XCTAssertEqual(mentions, ["johnsundell"])
    }
}

For more on async testing, check out "Unit testing asynchronous Swift code".

101 Adding support for Apple Pencil double-taps

✍️ Adding support for the new Apple Pencil double-tap feature is super easy! All you have to do is to create a UIPencilInteraction, add it to a view, and implement one delegate method. Hopefully all pencil-compatible apps will soon adopt this.

let interaction = UIPencilInteraction()
interaction.delegate = self
view.addInteraction(interaction)

extension ViewController: UIPencilInteractionDelegate {
    func pencilInteractionDidTap(_ interaction: UIPencilInteraction) {
        // Handle pencil double-tap
    }
}

For more on using this and other iPad Pro features, check out "Building iPad Pro features in Swift".

100 Combining values with functions

😎 Here's a cool function that combines a value with a function to return a closure that captures that value, so that it can be called without any arguments. Super useful when working with closure-based APIs and we want to use some of our properties without having to capture self.

func combine<A, B>(_ value: A, with closure: @escaping (A) -> B) -> () -> B {
    return { closure(value) }
}

// BEFORE:

class ProductViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        buyButton.handler = { [weak self] in
            guard let self = self else {
                return
            }
            
            self.productManager.startCheckout(for: self.product)
        }
    }
}

// AFTER:

class ProductViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        buyButton.handler = combine(product, with: productManager.startCheckout)
    }
}

99 Dependency injection using functions

💉 When I'm only using a single function from a dependency, I love to inject that function as a closure, instead of having to create a protocol and inject the whole object. Makes dependency injection & testing super simple.

final class ArticleLoader {
    typealias Networking = (Endpoint) -> Future<Data>
    
    private let networking: Networking
    
    init(networking: @escaping Networking = URLSession.shared.load) {
        self.networking = networking
    }
    
    func loadLatest() -> Future<[Article]> {
        return networking(.latestArticles).decode()
    }
}

For more on this technique, check out "Simple Swift dependency injection with functions".

98 Using a custom exception handler

💥 It's cool that you can easily assign a closure as a custom NSException handler. This is super useful when building things in Playgrounds - since you can't use breakpoints - so instead of just signal SIGABRT, you'll get the full exception description if something goes wrong.

NSSetUncaughtExceptionHandler { exception in
    print(exception)
}

97 Using type aliases to give semantic meaning to primitives

❤️ I love that in Swift, we can use the type system to make our code so much more self-documenting - one way of doing so is to use type aliases to give the primitive types that we use a more semantic meaning.

extension List.Item {
    // Using type aliases, we can give semantic meaning to the
    // primitive types that we use, without having to introduce
    // wrapper types.
    typealias Index = Int
}

extension List {
    enum Mutation {
        // Our enum cases now become a lot more self-documenting,
        // without having to add additional parameter labels to
        // explain them.
        case add(Item, Item.Index)
        case update(Item, Item.Index)
        case remove(Item.Index)
    }
}

For more on self-documenting code, check out "Writing self-documenting Swift code".

96 Specializing protocols using constraints

🤯 A little late night prototyping session reveals that protocol constraints can not only be applied to extensions - they can also be added to protocol definitions!

This is awesome, since it lets us easily define specialized protocols based on more generic ones.

protocol Component {
    associatedtype Container
    func add(to container: Container)
}

// Protocols that inherit from other protocols can include
// constraints to further specialize them.
protocol ViewComponent: Component where Container == UIView {
    associatedtype View: UIView
    var view: View { get }
}

extension ViewComponent {
    func add(to container: UIView) {
        container.addSubview(view)
    }
}

For more on specializing protocols, check out "Specializing protocols in Swift".

95 Unwrapping an optional or throwing an error

📦 Here's a super handy extension on Swift's Optional type, which gives us a really nice API for easily unwrapping an optional, or throwing an error in case the value turned out to be nil:

extension Optional {
    func orThrow(_ errorExpression: @autoclosure () -> Error) throws -> Wrapped {
        switch self {
        case .some(let value):
            return value
        case .none:
            throw errorExpression()
        }
    }
}

let file = try loadFile(at: path).orThrow(MissingFileError())

For more ways that optionals can be extended, check out "Extending optionals in Swift".

94 Testing code that uses static APIs

👩‍🔬 Testing code that uses static APIs can be really tricky, but there's a way that it can often be done - using Swift's first class function capabilities!

Instead of accessing that static API directly, we can inject the function we want to use, which enables us to mock it!

// BEFORE

class FriendsLoader {
    func loadFriends(then handler: @escaping (Result<[Friend]>) -> Void) {
        Networking.loadData(from: .friends) { result in
            ...
        }
    }
}

// AFTER

class FriendsLoader {
    typealias Handler<T> = (Result<T>) -> Void
    typealias DataLoadingFunction = (Endpoint, @escaping Handler<Data>) -> Void

    func loadFriends(using dataLoading: DataLoadingFunction = Networking.loadData,
                     then handler: @escaping Handler<[Friend]>) {
        dataLoading(.friends) { result in
            ...
        }
    }
}

// MOCKING IN TESTS

let dataLoading: FriendsLoader.DataLoadingFunction = { _, handler in
    handler(.success(mockData))
}

friendsLoader.loadFriends(using: dataLoading) { result in
    ...
}

93 Matching multiple enum cases with associated values

🐾 Swift's pattern matching capabilities are so powerful! Two enum cases with associated values can even be matched and handled by the same switch case - which is super useful when handling state changes with similar data.

enum DownloadState {
    case inProgress(progress: Double)
    case paused(progress: Double)
    case cancelled
    case finished(Data)
}

func downloadStateDidChange(to state: DownloadState) {
    switch state {
    case .inProgress(let progress), .paused(let progress):
        updateProgressView(with: progress)
    case .cancelled:
        showCancelledMessage()
    case .finished(let data):
        process(data)
    }
}

92 Multiline string literals

🅰 One really nice benefit of Swift multiline string literals - even for single lines of text - is that they don't require quotes to be escaped. Perfect when working with things like HTML, or creating a custom description for an object.

let html = highlighter.highlight("Array<String>")

XCTAssertEqual(html, """
<span class="type">Array</span>&lt;<span class="type">String</span>&gt;
""")

91 Reducing sequences

💎 While it's very common in functional programming, the reduce function might be a bit of a hidden gem in Swift. It provides a super useful way to transform a sequence into a single value.

extension Sequence where Element: Equatable {
    func numberOfOccurrences(of target: Element) -> Int {
        return reduce(0) { result, element in
            guard element == target else {
                return result
            }

            return result + 1
        }
    }
}

You can read more about transforming collections in "Transforming collections in Swift".

90 Avoiding manual Codable implementations

📦 When I use Codable in Swift, I want to avoid manual implementations as much as possible, even when there's a mismatch between my code structure and the JSON I'm decoding.

One way that can often be achieved is to use private data containers combined with computed properties.

struct User: Codable {
    let name: String
    let age: Int

    var homeTown: String { return originPlace.name }

    private let originPlace: Place
}

private extension User {
    struct Place: Codable {
        let name: String
    }
}

extension User {
    struct Container: Codable {
        let user: User
    }
}

89 Using feature flags instead of feature branches

🚢 Instead of using feature branches, I merge almost all of my code directly into master - and then I use feature flags to conditionally enable features when they're ready. That way I can avoid merge conflicts and keep shipping!

extension ListViewController {
    func addSearchIfNeeded() {
        // Rather than having to keep maintaining a separate
        // feature branch for a new feature, we can use a flag
        // to conditionally turn it on.
        guard FeatureFlags.searchEnabled else {
            return
        }

        let resultsVC = SearchResultsViewController()
        let searchVC = UISearchController(
            searchResultsController: resultsVC
        )

        searchVC.searchResultsUpdater = resultsVC
        navigationItem.searchController = searchVC
    }
}

You can read more about feature flags in "Feature flags in Swift".

88 Lightweight data hierarchies using tuples

💾 Here I'm using tuples to create a lightweight hierarchy for my data, giving me a nice structure without having to introduce any additional types.

struct CodeSegment {
    var tokens: (
        previous: String?,
        current: String
    )

    var delimiters: (
        previous: Character?
        next: Character?
    )
}

handle(segment.tokens.current)

You can read more about tuples in "Using tuples as lightweight types in Swift"

87 The rule of threes

3️⃣ Whenever I have 3 properties or local variables that share the same prefix, I usually try to extract them into their own method or type. That way I can avoid massive types & methods, and also increase readability, without falling into a "premature optimization" trap.

Before

public func generate() throws {
    let contentFolder = try folder.subfolder(named: "content")

    let articleFolder = try contentFolder.subfolder(named: "posts")
    let articleProcessor = ContentProcessor(folder: articleFolder)
    let articles = try articleProcessor.process()

    ...
}

After

public func generate() throws {
    let contentFolder = try folder.subfolder(named: "content")
    let articles = try processArticles(in: contentFolder)
    ...
}

private func processArticles(in folder: Folder) throws -> [ContentItem] {
    let folder = try folder.subfolder(named: "posts")
    let processor = ContentProcessor(folder: folder)
    return try processor.process()
}

86 Useful Codable extensions

👨‍🔧 Here's two extensions that I always add to the Encodable & Decodable protocols, which for me really make the Codable API nicer to use. By using type inference for decoding, a lot of boilerplate can be removed when the compiler is already able to infer the resulting type.

extension Encodable {
    func encoded() throws -> Data {
        return try JSONEncoder().encode(self)
    }
}

extension Data {
    func decoded<T: Decodable>() throws -> T {
        return try JSONDecoder().decode(T.self, from: self)
    }
}

let data = try user.encoded()

// By using a generic type in the decoded() method, the
// compiler can often infer the type we want to decode
// from the current context.
try userDidLogin(data.decoded())

// And if not, we can always supply the type, still making
// the call site read very nicely.
let otherUser = try data.decoded() as User

85 Using shared UserDefaults suites

📦 UserDefaults is a lot more powerful than what it first might seem like. Not only can it store more complex values (like dates & dictionaries) and parse command line arguments - it also enables easy sharing of settings & lightweight data between apps in the same App Group.

let sharedDefaults = UserDefaults(suiteName: "my-app-group")!
let useDarkMode = sharedDefaults.bool(forKey: "dark-mode")

// This value is put into the shared suite.
sharedDefaults.set(true, forKey: "dark-mode")

// If you want to treat the shared settings as read-only (and add
// local overrides on top of them), you can simply add the shared
// suite to the standard UserDefaults.
let combinedDefaults = UserDefaults.standard
combinedDefaults.addSuite(named: "my-app-group")

// This value is a local override, not added to the shared suite.
combinedDefaults.set(true, forKey: "app-specific-override")

84 Custom UIView backing layers

🎨 By overriding layerClass you can tell UIKit what CALayer class to use for a UIView's backing layer. That way you can reduce the amount of layers, and don't have to do any manual layout.

final class GradientView: UIView {
    override class var layerClass: AnyClass { return CAGradientLayer.self }

    var colors: (start: UIColor, end: UIColor)? {
        didSet { updateLayer() }
    }

    private func updateLayer() {
        let layer = self.layer as! CAGradientLayer
        layer.colors = colors.map { [$0.start.cgColor, $0.end.cgColor] }
    }
}

83 Auto-Equatable enums with associated values

✅ That the compiler now automatically synthesizes Equatable conformances is such a huge upgrade for Swift! And the cool thing is that it works for all kinds of types - even for enums with associated values! Especially useful when using enums for verification in unit tests.

struct Article: Equatable {
    let title: String
    let text: String
}

struct User: Equatable {
    let name: String
    let age: Int
}

extension Navigator {
    enum Destination: Equatable {
        case profile(User)
        case article(Article)
    }
}

func testNavigatingToArticle() {
    let article = Article(title: "Title", text: "Text")
    controller.select(article)
    XCTAssertEqual(navigator.destinations, [.article(article)])
}

82 Defaults for associated types

🤝 Associated types can have defaults in Swift - which is super useful for types that are not easily inferred (for example when they're not used for a specific instance method or property).

protocol Identifiable {
    associatedtype RawIdentifier: Codable = String

    var id: Identifier<Self> { get }
}

struct User: Identifiable {
    let id: Identifier<User>
    let name: String
}

struct Group: Identifiable {
    typealias RawIdentifier = Int

    let id: Identifier<Group>
    let name: String
}

81 Creating a dedicated identifier type

🆔 If you want to avoid using plain strings as identifiers (which can increase both type safety & readability), it's really easy to create a custom Identifier type that feels just like a native Swift type, thanks to protocols!

More on this topic in "Type-safe identifiers in Swift".

struct Identifier: Hashable {
    let string: String
}

extension Identifier: ExpressibleByStringLiteral {
    init(stringLiteral value: String) {
        string = value
    }
}

extension Identifier: CustomStringConvertible {
    var description: String {
        return string
    }
}

extension Identifier: Codable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        string = try container.decode(String.self)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(string)
    }
}

struct Article: Codable {
    let id: Identifier
    let title: String
}

let article = Article(id: "my-article", title: "Hello world!")

80 Assigning optional tuple members to variables

🙌 A really cool thing about using tuples to model the internal state of a Swift type, is that you can unwrap an optional tuple's members directly into local variables.

Very useful in order to group multiple optional values together for easy unwrapping & handling.

class ImageTransformer {
    private var queue = [(image: UIImage, transform: Transform)]()

    private func processNext() {
        // When unwrapping an optional tuple, you can assign the members
        // directly to local variables.
        guard let (image, transform) = queue.first else {
            return
        }

        let context = Context()
        context.draw(image)
        context.apply(transform)
        ...
    }
}

79 Struct convenience initializers

❤️ I love to structure my code using extensions in Swift. One big benefit of doing so when it comes to struct initializers, is that defining a convenience initializer doesn't remove the default one the compiler generates - best of both worlds!

struct Article {
    let date: Date
    var title: String
    var text: String
    var comments: [Comment]
}

extension Article {
    init(title: String, text: String) {
        self.init(date: Date(), title: title, text: text, comments: [])
    }
}

let articleA = Article(title: "Best Cupcake Recipe", text: "...")

let articleB = Article(
    date: Date(),
    title: "Best Cupcake Recipe",
    text: "...",
    comments: [
        Comment(user: currentUser, text: "Yep, can confirm!")
    ]
)

78 Usages of throwing functions

🏈 A big benefit of using throwing functions for synchronous Swift APIs is that the caller can decide whether they want to treat the return value as optional (try?) or required (try).

func loadFile(named name: String) throws -> File {
    guard let url = urlForFile(named: name) else {
        throw File.Error.missing
    }

    do {
        let data = try Data(contentsOf: url)
        return File(url: url, data: data)
    } catch {
        throw File.Error.invalidData(error)
    }
}

let requiredFile = try loadFile(named: "AppConfig.json")

let optionalFile = try? loadFile(named: "UserSettings.json")

77 Nested generic types

🐝 Types that are nested in generics automatically inherit their parent's generic types - which is super useful when defining accessory types (for things like states or outcomes).

struct Task<Input, Output> {
    typealias Closure = (Input) throws -> Output

    let closure: Closure
}

extension Task {
    enum Result {
        case success(Output)
        case failure(Error)
    }
}

76 Equatable & Hashable structures

🤖 Now that the Swift compiler automatically synthesizes Equatable & Hashable conformances for value types, it's easier than ever to setup model structures with nested types that are all Equatable/Hashable!

typealias Value = Hashable & Codable

struct User: Value {
    var name: String
    var age: Int
    var lastLoginDate: Date?
    var settings: Settings
}

extension User {
    struct Settings: Value {
        var itemsPerPage: Int
        var theme: Theme
    }
}

extension User.Settings {
    enum Theme: String, Value {
        case light
        case dark
    }
}

You can read more about using nested types in Swift here.

75 Conditional conformances

🎉 Swift 4.1 is here! One of the key features it brings is conditional conformances, which lets you have a type only conform to a protocol under certain constraints.

protocol UnboxTransformable {
    associatedtype RawValue

    static func transform(_ value: RawValue) throws -> Self?
}

extension Array: UnboxTransformable where Element: UnboxTransformable {
    typealias RawValue = [Element.RawValue]

    static func transform(_ value: RawValue) throws -> [Element]? {
        return try value.compactMap(Element.transform)
    }
}

I also have an article with lots of more info on conditional conformances here. Paul Hudson also has a great overview of all Swift 4.1 features here.

74 Generic type aliases

🕵️‍♀️ A cool thing about Swift type aliases is that they can be generic! Combine that with tuples and you can easily define simple generic types.

typealias Pair<T> = (T, T)

extension Game {
    func calculateScore(for players: Pair<Player>) -> Int {
        ...
    }
}

You can read more about using tuples as lightweight types here.

73 Parsing command line arguments using UserDefaults

☑️ A really cool "hidden" feature of UserDefaults is that it contains any arguments that were passed to the app at launch!

Super useful both in Swift command line tools & scripts, but also to temporarily override a value when debugging iOS apps.

let defaults = UserDefaults.standard
let query = defaults.string(forKey: "query")
let resultCount = defaults.integer(forKey: "results")

72 Using the & operator

👏 Swift's & operator is awesome! Not only can you use it to compose protocols, you can compose other types too! Very useful if you want to hide concrete types & implementation details.

protocol LoadableFromURL {
    func load(from url: URL)
}

class ContentViewController: UIViewController, LoadableFromURL {
    func load(from url: URL) {
        ...
    }
}

class ViewControllerFactory {
    func makeContentViewController() -> UIViewController & LoadableFromURL {
        return ContentViewController()
    }
}

71 Capturing multiple values in mocks

🤗 When capturing values in mocks, using an array (instead of just a single value) makes it easy to verify that only a certain number of values were passed.

Perfect for protecting against "over-calling" something.

class UserManagerTests: XCTestCase {
    func testObserversCalledWhenUserFirstLogsIn() {
        let manager = UserManager()

        let observer = ObserverMock()
        manager.addObserver(observer)

        // First login, observers should be notified
        let user = User(id: 123, name: "John")
        manager.userDidLogin(user)
        XCTAssertEqual(observer.users, [user])

        // If the same user logs in again, observers shouldn't be notified
        manager.userDidLogin(user)
        XCTAssertEqual(observer.users, [user])
    }
}

private extension UserManagerTests {
    class ObserverMock: UserManagerObserver {
        private(set) var users = [User]()

        func userDidChange(to user: User) {
            users.append(user)
        }
    }
}

70 Reducing the need for mocks

👋 When writing tests, you don't always need to create mocks - you can create stubs using real instances of things like errors, URLs & UserDefaults.

Here's how to do that for some common tasks/object types in Swift:

// Create errors using NSError (#function can be used to reference the name of the test)
let error = NSError(domain: #function, code: 1, userInfo: nil)

// Create non-optional URLs using file paths
let url = URL(fileURLWithPath: "Some/URL")

// Reference the test bundle using Bundle(for:)
let bundle = Bundle(for: type(of: self))

// Create an explicit UserDefaults object (instead of having to use a mock)
let userDefaults = UserDefaults(suiteName: #function)

// Create queues to control/await concurrent operations
let queue = DispatchQueue(label: #function)

For when you actually do need mocking, check out "Mocking in Swift".

69 Using "then" as an external parameter label for closures

⏱ I've started using "then" as an external parameter label for completion handlers. Makes the call site read really nicely (Because I do ❤️ conversational API design) regardless of whether trailing closure syntax is used or not.

protocol DataLoader {
    // Adding type aliases to protocols can be a great way to
    // reduce verbosity for parameter types.
    typealias Handler = (Result<Data>) -> Void
    associatedtype Endpoint

    func loadData(from endpoint: Endpoint, then handler: @escaping Handler)
}

loader.loadData(from: .messages) { result in
    ...
}

loader.loadData(from: .messages, then: { result in
    ...
})

68 Combining lazily evaluated sequences with the builder pattern

😴 Combining lazily evaluated sequences with builder pattern-like properties can lead to some pretty sweet APIs for configurable sequences in Swift.

Also useful for queries & other things you "build up" and then execute.

// Extension adding builder pattern-like properties that return
// a new sequence value with the given configuration applied
extension FileSequence {
    var recursive: FileSequence {
        var sequence = self
        sequence.isRecursive = true
        return sequence
    }

    var includingHidden: FileSequence {
        var sequence = self
        sequence.includeHidden = true
        return sequence
    }
}

// BEFORE

let files = folder.makeFileSequence(recursive: true, includeHidden: true)

// AFTER

let files = folder.files.recursive.includingHidden

Want an intro to lazy sequences? Check out "Swift sequences: The art of being lazy".

67 Faster & more stable UI tests

My top 3 tips for faster & more stable UI tests:

📱 Reset the app's state at the beginning of every test.

🆔 Use accessibility identifiers instead of UI strings.

⏱ Use expectations instead of waiting time.

func testOpeningArticle() {
    // Launch the app with an argument that tells it to reset its state
    let app = XCUIApplication()
    app.launchArguments.append("--uitesting")
    app.launch()
    
    // Check that the app is displaying an activity indicator
    let activityIndicator = app.activityIndicator.element
    XCTAssertTrue(activityIndicator.exists)
    
    // Wait for the loading indicator to disappear = content is ready
    expectation(for: NSPredicate(format: "exists == 0"),
                evaluatedWith: activityIndicator)
                
    // Use a generous timeout in case the network is slow
    waitForExpectations(timeout: 10)
    
    // Tap the cell for the first article
    app.tables.cells["Article.0"].tap()
    
    // Assert that a label with the accessibility identifier "Article.Title" exists
    let label = app.staticTexts["Article.Title"]
    XCTAssertTrue(label.exists)
}

66 Accessing the clipboard from a Swift script

📋 It's super easy to access the contents of the clipboard from a Swift script. A big benefit of Swift scripting is being able to use Cocoa's powerful APIs for Mac apps.

import Cocoa

let clipboard = NSPasteboard.general.string(forType: .string)

65 Using tuples for view state

🎯 Using Swift tuples for view state can be a super nice way to group multiple properties together and render them reactively using the layout system.

By using a tuple we don't have to either introduce a new type or make our view model-aware.

class TextView: UIView {
    var state: (title: String?, text: String?) {
        // By telling UIKit that our view needs layout and binding our
        // state in layoutSubviews, we can react to state changes without
        // doing unnecessary layout work.
        didSet { setNeedsLayout() }
    }

    private let titleLabel = UILabel()
    private let textLabel = UILabel()

    override func layoutSubviews() {
        super.layoutSubviews()

        titleLabel.text = state.title
        textLabel.text = state.text

        ...
    }
}

64 Throwing tests and LocalizedError

⚾️ Swift tests can throw, which is super useful in order to avoid complicated logic or force unwrapping. By making errors conform to LocalizedError, you can also get a nice error message in Xcode if there's a failure.

class ImageCacheTests: XCTestCase {
    func testCachingAndLoadingImage() throws {
        let bundle = Bundle(for: type(of: self))
        let cache = ImageCache(bundle: bundle)
        
        // Bonus tip: You can easily load images from your test
        // bundle using this UIImage initializer
        let image = try require(UIImage(named: "sample", in: bundle, compatibleWith: nil))
        try cache.cache(image, forKey: "key")
        
        let cachedImage = try cache.image(forKey: "key")
        XCTAssertEqual(image, cachedImage)
    }
}

enum ImageCacheError {
    case emptyKey
    case dataConversionFailed
}

// When using throwing tests, making your errors conform to
// LocalizedError will render a much nicer error message in
// Xcode (per default only the error code is shown).
extension ImageCacheError: LocalizedError {
    var errorDescription: String? {
        switch self {
        case .emptyKey:
            return "An empty key was given"
        case .dataConversionFailed:
            return "Failed to convert the given image to Data"
        }
    }
}

For more information, and the implementation of the require method used above, check out "Avoiding force unwrapping in Swift unit tests".

63 The difference between static and class properties

✍️ Unlike static properties, class properties can be overridden by subclasses (however, they can't be stored, only computed).

class TableViewCell: UITableViewCell {
    class var preferredHeight: CGFloat { return 60 }
}

class TallTableViewCell: TableViewCell {
    override class var preferredHeight: CGFloat { return 100 }
}

62 Creating extensions with static factory methods

👨‍🎨 Creating extensions with static factory methods can be a great alternative to subclassing in Swift, especially for things like setting up UIViews, CALayers or other kinds of styling.

It also lets you remove a lot of styling & setup from your view controllers.

extension UILabel {
    static func makeForTitle() -> UILabel {
        let label = UILabel()
        label.font = .boldSystemFont(ofSize: 24)
        label.textColor = .darkGray
        label.adjustsFontSizeToFitWidth = true
        label.minimumScaleFactor = 0.75
        return label
    }

    static func makeForText() -> UILabel {
        let label = UILabel()
        label.font = .systemFont(ofSize: 16)
        label.textColor = .black
        label.numberOfLines = 0
        return label
    }
}

class ArticleViewController: UIViewController {
    lazy var titleLabel = UILabel.makeForTitle()
    lazy var textLabel = UILabel.makeForText()
}

61 Child view controller auto-resizing

🧒 An awesome thing about child view controllers is that they're automatically resized to match their parent, making them a super nice solution for things like loading & error views.

class ListViewController: UIViewController {
    func loadItems() {
        let loadingViewController = LoadingViewController()
        add(loadingViewController)

        dataLoader.loadItems { [weak self] result in
            loadingViewController.remove()
            self?.handle(result)
        }
    }
}

For more about child view controller (including the add and remove methods used above), check out "Using child view controllers as plugins in Swift".

60 Using zip

🤐 Using the zip function in Swift you can easily combine two sequences. Super useful when using two sequences to do some work, since zip takes care of all the bounds-checking.

func render(titles: [String]) {
    for (label, text) in zip(titleLabels, titles) {
        print(text)
        label.text = text
    }
}

59 Defining custom option sets

🎛 The awesome thing about option sets in Swift is that they can automatically either be passed as a single member or as a set. Even cooler is that you can easily define your own option sets as well, perfect for options and other non-exclusive values.

// Option sets are awesome, because you can easily pass them
// both using dot syntax and array literal syntax, like when
// using the UIView animation API:
UIView.animate(withDuration: 0.3,
               delay: 0,
               options: .allowUserInteraction,
               animations: animations)

UIView.animate(withDuration: 0.3,
               delay: 0,
               options: [.allowUserInteraction, .layoutSubviews],
               animations: animations)

// The cool thing is that you can easily define your own option
// sets as well, by defining a struct that has an Int rawValue,
// that will be used as a bit mask.
extension Cache {
    struct Options: OptionSet {
        static let saveToDisk = Options(rawValue: 1)
        static let clearOnMemoryWarning = Options(rawValue: 1 << 1)
        static let clearDaily = Options(rawValue: 1 << 2)

        let rawValue: Int
    }
}

// We can now use Cache.Options just like UIViewAnimationOptions:
Cache(options: .saveToDisk)
Cache(options: [.saveToDisk, .clearDaily])

58 Using the where clause with associated types

🙌 Using the where clause when designing protocol-oriented APIs in Swift can let your implementations (or others' if it's open source) have a lot more freedom, especially when it comes to collections.

See "Using generic type constraints in Swift 4" for more info.

public protocol PathFinderMap {
    associatedtype Node
    // Using the 'where' clause for associated types, we can
    // ensure that a type meets certain requirements (in this
    // case that it's a sequence with Node elements).
    associatedtype NodeSequence: Sequence where NodeSequence.Element == Node

    // Instead of using a concrete type (like [Node]) here, we
    // give implementors of this protocol more freedom while
    // still meeting our requirements. For example, one
    // implementation might use Set<Node>.
    func neighbors(of node: Node) -> NodeSequence
}

57 Using first class functions when iterating over a dictionary

👨‍🍳 Combine first class functions in Swift with the fact that Dictionary elements are (Key, Value) tuples and you can build yourself some pretty awesome functional chains when iterating over a Dictionary.

func makeActor(at coordinate: Coordinate, for building: Building) -> Actor {
    let actor = Actor()
    actor.position = coordinate.point
    actor.animation = building.animation
    return actor
}

func render(_ buildings: [Coordinate : Building]) {
    buildings.map(makeActor).forEach(add)
}

56 Calling instance methods as static functions

😎 In Swift, you can call any instance method as a static function and it will return a closure representing that method. This is how running tests using SPM on Linux works.

More about this topic in my blog post "First class functions in Swift".

// This produces a '() -> Void' closure which is a reference to the
// given view's 'removeFromSuperview' method.
let closure = UIView.removeFromSuperview(view)

// We can now call it just like we would any other closure, and it
// will run 'view.removeFromSuperview()'
closure()

// This is how running tests using the Swift Package Manager on Linux
// works, you return your test functions as closures:
extension UserManagerTests {
    static var allTests = [
        ("testLoggingIn", testLoggingIn),
        ("testLoggingOut", testLoggingOut),
        ("testUserPermissions", testUserPermissions)
    ]
}

55 Dropping suffixes from method names to support multiple arguments

👏 One really nice benefit of dropping suffixes from method names (and just using verbs, when possible) is that it becomes super easy to support both single and multiple arguments, and it works really well semantically.

extension UIView {
    func add(_ subviews: UIView...) {
        subviews.forEach(addSubview)
    }
}

view.add(button)
view.add(label)

// By dropping the "Subview" suffix from the method name, both
// single and multiple arguments work really well semantically.
view.add(button, label)

54 Constraining protocols to classes to ensure mutability

👽 Using the AnyObject (or class) constraint on protocols is not only useful when defining delegates (or other weak references), but also when you always want instances to be mutable without copying.

// By constraining a protocol with 'AnyObject' it can only be adopted
// by classes, which means all instances will always be mutable, and
// that it's the original instance (not a copy) that will be mutated.
protocol DataContainer: AnyObject {
    var data: Data? { get set }
}

class UserSettingsManager {
    private var settings: Settings
    private let dataContainer: DataContainer

    // Since DataContainer is a protocol, we an easily mock it in
    // tests if we use dependency injection
    init(settings: Settings, dataContainer: DataContainer) {
        self.settings = settings
        self.dataContainer = dataContainer
    }

    func saveSettings() throws {
        let data = try settings.serialize()

        // We can now assign properties on an instance of our protocol
        // because the compiler knows it's always going to be a class
        dataContainer.data = data
    }
}

53 String-based enums in string interpolation

🍣 Even if you define a custom raw value for a string-based enum in Swift, the full case name will be used in string interpolation.

Super useful when using separate raw values for JSON, while still wanting to use the full case name in other contexts.

extension Building {
    // This enum has custom raw values that are used when decoding
    // a value, for example from JSON.
    enum Kind: String {
        case castle = "C"
        case town = "T"
        case barracks = "B"
        case goldMine = "G"
        case camp = "CA"
        case blacksmith = "BL"
    }

    var animation: Animation {
        return Animation(
            // When used in string interpolation, the full case name is still used.
            // For 'castle' this will be 'buildings/castle'.
            name: "buildings/\(kind)",
            frameCount: frameCount,
            frameDuration: frameDuration
        )
    }
}

52 Expressively comparing a value with a list of candidates

👨‍🔬 Continuing to experiment with expressive ways of comparing a value with a list of candidates in Swift. Adding an extension on Equatable is probably my favorite approach so far.

extension Equatable {
    func isAny(of candidates: Self...) -> Bool {
        return candidates.contains(self)
    }
}

let isHorizontal = direction.isAny(of: .left, .right)

See tip 35 for my previous experiment.

51 UIView bounds and transforms

📐 A really interesting side-effect of a UIView's bounds being its rect within its own coordinate system is that transforms don't affect it at all. That's why it's usually a better fit than frame when doing layout calculations of subviews.

let view = UIView()
view.frame.size = CGSize(width: 100, height: 100)
view.transform = CGAffineTransform(scaleX: 2, y: 2)

print(view.frame) // (-50.0, -50.0, 200.0, 200.0)
print(view.bounds) // (0.0, 0.0, 100.0, 100.0)

50 UIKit default arguments

👏 It's awesome that many UIKit APIs with completion handlers and other optional parameters import into Swift with default arguments (even though they are written in Objective-C). Getting rid of all those nil arguments is so nice!

// BEFORE: All parameters are specified, just like in Objective-C

viewController.present(modalViewController, animated: true, completion: nil)

modalViewController.dismiss(animated: true, completion: nil)

viewController.transition(from: loadingViewController,
                          to: contentViewController,
                          duration: 0.3,
                          options: [],
                          animations: animations,
                          completion: nil)

// AFTER: Since many UIKit APIs with completion handlers and other
// optional parameters import into Swift with default arguments,
// we can make our calls shorter

viewController.present(modalViewController, animated: true)

modalViewController.dismiss(animated: true)

viewController.transition(from: loadingViewController,
                          to: contentViewController,
                          duration: 0.3,
                          animations: animations)

49 Avoiding Massive View Controllers

✂️ Avoiding Massive View Controllers is all about finding the right levels of abstraction and splitting things up.

My personal rule of thumb is that as soon as I have 3 methods or properties that have the same prefix, I break them out into their own type.

// BEFORE

class LoginViewController: UIViewController {
    private lazy var signUpLabel = UILabel()
    private lazy var signUpImageView = UIImageView()
    private lazy var signUpButton = UIButton()
}

// AFTER

class LoginViewController: UIViewController {
    private lazy var signUpView = SignUpView()
}

class SignUpView: UIView {
    private lazy var label = UILabel()
    private lazy var imageView = UIImageView()
    private lazy var button = UIButton()
}

48 Extending optionals

❤️ I love the fact that optionals are enums in Swift - it makes it so easy to extend them with convenience APIs for certain types. Especially useful when doing things like data validation on optional values.

func validateTextFields() -> Bool {
    guard !usernameTextField.text.isNilOrEmpty else {
        return false
    }

    ...

    return true
}

// Since all optionals are actual enum values in Swift, we can easily
// extend them for certain types, to add our own convenience APIs

extension Optional where Wrapped == String {
    var isNilOrEmpty: Bool {
        switch self {
        case let string?:
            return string.isEmpty
        case nil:
            return true
        }
    }
}

// Since strings are now Collections in Swift 4, you can even
// add this property to all optional collections:

extension Optional where Wrapped: Collection {
    var isNilOrEmpty: Bool {
        switch self {
        case let collection?:
            return collection.isEmpty
        case nil:
            return true
        }
    }
}

47 Using where with for-loops

🗺 Using the where keyword can be a super nice way to quickly apply a filter in a for-loop in Swift. You can of course use map, filter and forEach, or guard, but for simple loops I think this is very expressive and nice.

func archiveMarkedPosts() {
    for post in posts where post.isMarked {
        archive(post)
    }
}

func healAllies() {
    for player in players where player.isAllied(to: currentPlayer) {
        player.heal()
    }
}

46 Variable shadowing

👻 Variable shadowing can be super useful in Swift, especially when you want to create a local copy of a parameter value in order to use it as state within a closure.

init(repeatMode: RepeatMode, closure: @escaping () -> UpdateOutcome) {
    // Shadow the argument with a local, mutable copy
    var repeatMode = repeatMode
    
    self.closure = {
        // With shadowing, there's no risk of accidentially
        // referring to the immutable version
        switch repeatMode {
        case .forever:
            break
        case .times(let count):
            guard count > 0 else {
                return .finished
            }
            
            // We can now capture the mutable version and use
            // it for state in a closure
            repeatMode = .times(count - 1)
        }
        
        return closure()
    }
}

45 Using dot syntax for static properties and initializers

✒️ Dot syntax is one of my favorite features of Swift. What's really cool is that it's not only for enums, any static method or property can be used with dot syntax - even initializers! Perfect for convenience APIs and default parameters.

public enum RepeatMode {
    case times(Int)
    case forever
}

public extension RepeatMode {
    static var never: RepeatMode {
        return .times(0)
    }

    static var once: RepeatMode {
        return .times(1)
    }
}

view.perform(animation, repeated: .once)

// To make default parameters more compact, you can even use init with dot syntax

class ImageLoader {
    init(cache: Cache = .init(), decoder: ImageDecoder = .init()) {
        ...
    }
}

44 Calling functions as closures with a tuple as parameters

🚀 One really cool aspect of Swift having first class functions is that you can pass any function (or even initializer) as a closure, and even call it with a tuple containing its parameters!

// This function lets us treat any "normal" function or method as
// a closure and run it with a tuple that contains its parameters
func call<Input, Output>(_ function: (Input) -> Output, with input: Input) -> Output {
    return function(input)
}

class ViewFactory {
    func makeHeaderView() -> HeaderView {
        // We can now pass an initializer as a closure, and a tuple
        // containing its parameters
        return call(HeaderView.init, with: loadTextStyles())
    }
    
    private func loadTextStyles() -> (font: UIFont, color: UIColor) {
        return (theme.font, theme.textColor)
    }
}

class HeaderView {
    init(font: UIFont, textColor: UIColor) {
        ...
    }
}

43 Enabling static dependency injection

💉 If you've been struggling to test code that uses static APIs, here's a technique you can use to enable static dependency injection without having to modify any call sites:

// Before: Almost impossible to test due to the use of singletons

class Analytics {
    static func log(_ event: Event) {
        Database.shared.save(event)
        
        let dictionary = event.serialize()
        NetworkManager.shared.post(dictionary, to: eventURL)
    }
}

// After: Much easier to test, since we can inject mocks as arguments

class Analytics {
    static func log(_ event: Event,
                    database: Database = .shared,
                    networkManager: NetworkManager = .shared) {
        database.save(event)
        
        let dictionary = event.serialize()
        networkManager.post(dictionary, to: eventURL)
    }
}

42 Type inference for lazy properties in Swift 4

🎉 In Swift 4, type inference works for lazy properties and you don't need to explicitly refer to self!

// Swift 3

class PurchaseView: UIView {
    private lazy var buyButton: UIButton = self.makeBuyButton()
    
    private func makeBuyButton() -> UIButton {
        let button = UIButton()
        button.setTitle("Buy", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        return button
    }
}

// Swift 4

class PurchaseView: UIView {
    private lazy var buyButton = makeBuyButton()
    
    private func makeBuyButton() -> UIButton {
        let button = UIButton()
        button.setTitle("Buy", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        return button
    }
}

41 Converting Swift errors to NSError

😎 You can turn any Swift Error into an NSError, which is super useful when pattern matching with a code 👍. Also, switching on optionals is pretty cool!

let task = urlSession.dataTask(with: url) { data, _, error in
    switch error {
    case .some(let error as NSError) where error.code == NSURLErrorNotConnectedToInternet:
        presenter.showOfflineView()
    case .some(let error):
        presenter.showGenericErrorView()
    case .none:
        presenter.renderContent(from: data)
    }
}

task.resume()

Also make sure to check out Kostas Kremizas' tip about how you can pattern match directly against a member of URLError.

40 Making UIImage macOS compatible

🖥 Here's an easy way to make iOS model code that uses UIImage macOS compatible - like me and Gui Rambo discussed on the Swift by Sundell Podcast.

// Either put this in a separate file that you only include in your macOS target or wrap the code in #if os(macOS) / #endif

import Cocoa

// Step 1: Typealias UIImage to NSImage
typealias UIImage = NSImage

// Step 2: You might want to add these APIs that UIImage has but NSImage doesn't.
extension NSImage {
    var cgImage: CGImage? {
        var proposedRect = CGRect(origin: .zero, size: size)

        return cgImage(forProposedRect: &proposedRect,
                       context: nil,
                       hints: nil)
    }

    convenience init?(named name: String) {
        self.init(named: Name(name))
    }
}

// Step 3: Profit - you can now make your model code that uses UIImage cross-platform!
struct User {
    let name: String
    let profileImage: UIImage
}

39 Internally mutable protocol-oriented APIs

🤖 You can easily define a protocol-oriented API that can only be mutated internally, by using an internal protocol that extends a public one.

// Declare a public protocol that acts as your immutable API
public protocol ModelHolder {
    associatedtype Model
    var model: Model { get }
}

// Declare an extended, internal protocol that provides a mutable API
internal protocol MutableModelHolder: ModelHolder {
    var model: Model { get set }
}

// You can now implement the requirements using 'public internal(set)'
public class UserHolder: MutableModelHolder {
    public internal(set) var model: User

    internal init(model: User) {
        self.model = model
    }
}

38 Switching on a set

🎛 You can switch on a set using array literals as cases in Swift! Can be really useful to avoid many if/else if statements.

class RoadTile: Tile {
    var connectedDirections = Set<Direction>()

    func render() {
        switch connectedDirections {
        case [.up, .down]:
            image = UIImage(named: "road-vertical")
        case [.left, .right]:
            image = UIImage(named: "road-horizontal")
        default:
            image = UIImage(named: "road")
        }
    }
}

37 Adding the current locale to cache keys

🌍 When caching localized content in an app, it's a good idea to add the current locale to all keys, to prevent bugs when switching languages.

func cache(_ content: Content, forKey key: String) throws {
    let data = try wrap(content) as Data
    let key = localize(key: key)
    try storage.store(data, forKey: key)
}

func loadCachedContent(forKey key: String) -> Content? {
    let key = localize(key: key)
    let data = storage.loadData(forKey: key)
    return data.flatMap { try? unbox(data: $0) }
}

private func localize(key: String) -> String {
    return key + "-" + Bundle.main.preferredLocalizations[0]
}

36 Setting up tests to avoid retain cycles with weak references

🚳 Here's an easy way to setup a test to avoid accidental retain cycles with object relationships (like weak delegates & observers) in Swift:

func testDelegateNotRetained() {
    // Assign the delegate (weak) and also retain it using a local var
    var delegate: Delegate? = DelegateMock()
    controller.delegate = delegate
    XCTAssertNotNil(controller.delegate)
    
    // Release the local var, which should also release the weak reference
    delegate = nil
    XCTAssertNil(controller.delegate)
}

35 Expressively matching a value against a list of candidates

👨‍🔬 Playing around with an expressive way to check if a value matches any of a list of candidates in Swift:

// Instead of multiple conditions like this:

if string == "One" || string == "Two" || string == "Three" {

}

// You can now do:

if string == any(of: "One", "Two", "Three") {

}

You can find a gist with the implementation here.

34 Organizing code using extensions

👪 APIs in a Swift extension automatically inherit its access control level, making it a neat way to organize public, internal & private APIs.

public extension Animation {
    init(textureNamed textureName: String) {
        frames = [Texture(name: textureName)]
    }
    
    init(texturesNamed textureNames: [String], frameDuration: TimeInterval = 1) {
        frames = textureNames.map(Texture.init)
        self.frameDuration = frameDuration
    }
    
    init(image: Image) {
        frames = [Texture(image: image)]
    }
}

internal extension Animation {
    func loadFrameImages() -> [Image] {
        return frames.map { $0.loadImageIfNeeded() }
    }
}

33 Using map to transform an optional into a Result type

🗺 Using map you can transform an optional value into an optional Result type by simply passing in the enum case.

enum Result<Value> {
    case value(Value)
    case error(Error)
}

class Promise<Value> {
    private var result: Result<Value>?
    
    init(value: Value? = nil) {
        result = value.map(Result.value)
    }
}

32 Assigning to self in struct initializers

👌 It's so nice that you can assign directly to self in struct initializers in Swift. Very useful when adding conformance to protocols.

extension Bool: AnswerConvertible {
    public init(input: String) throws {
        switch input.lowercased() {
        case "y", "yes", "👍":
            self = true
        default:
            self = false
        }
    }
}

31 Recursively calling closures as inline functions

☎️ Defining Swift closures as inline functions enables you to recursively call them, which is super useful in things like custom sequences.

class Database {
    func records(matching query: Query) -> AnySequence<Record> {
        var recordIterator = loadRecords().makeIterator()
        
        func iterate() -> Record? {
            guard let nextRecord = recordIterator.next() else {
                return nil
            }
            
            guard nextRecord.matches(query) else {
                // Since the closure is an inline function, it can be recursively called,
                // in this case in order to advance to the next item.
                return iterate()
            }
            
            return nextRecord
        }
        
        // AnySequence/AnyIterator are part of the standard library and provide an easy way
        // to define custom sequences using closures.
        return AnySequence { AnyIterator(iterate) }
    }
}

Rob Napier points out that using the above might cause crashes if used on a large databaset, since Swift has no guaranteed Tail Call Optimization (TCO).

Slava Pestov also points out that another benefit of inline functions vs closures is that they can have their own generic parameter list.

30 Passing self to required Objective-C dependencies

🏖 Using lazy properties in Swift, you can pass self to required Objective-C dependencies without having to use force-unwrapped optionals.

class DataLoader: NSObject {
    lazy var urlSession: URLSession = self.makeURLSession()
    
    private func makeURLSession() -> URLSession {
        return URLSession(configuration: .default, delegate: self, delegateQueue: .main)
    }
}

class Renderer {
    lazy var displayLink: CADisplayLink = self.makeDisplayLink()
    
    private func makeDisplayLink() -> CADisplayLink {
        return CADisplayLink(target: self, selector: #selector(screenDidRefresh))
    }
}

29 Making weak or lazy properties readonly

👓 If you have a property in Swift that needs to be weak or lazy, you can still make it readonly by using private(set).

class Node {
    private(set) weak var parent: Node?
    private(set) lazy var children = [Node]()

    func add(child: Node) {
        children.append(child)
        child.parent = self
    }
}

28 Defining static URLs using string literals

🌏 Tired of using URL(string: "url")! for static URLs? Make URL conform to ExpressibleByStringLiteral and you can now simply use "url" instead.

extension URL: ExpressibleByStringLiteral {
    // By using 'StaticString' we disable string interpolation, for safety
    public init(stringLiteral value: StaticString) {
        self = URL(string: "\(value)").require(hint: "Invalid URL string literal: \(value)")
    }
}

// We can now define URLs using static string literals 🎉
let url: URL = "https://www.swiftbysundell.com"
let task = URLSession.shared.dataTask(with: "https://www.swiftbysundell.com")

// In Swift 3 or earlier, you also have to implement 2 additional initializers
extension URL {
    public init(extendedGraphemeClusterLiteral value: StaticString) {
        self.init(stringLiteral: value)
    }

    public init(unicodeScalarLiteral value: StaticString) {
        self.init(stringLiteral: value)
    }
}

To find the extension that adds the require() method on Optional that I use above, check out Require.

27 Manipulating points, sizes and frames using math operators

✚ I'm always careful with operator overloading, but for manipulating things like sizes, points & frames I find them super useful.

extension CGSize {
    static func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
        return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
    }
}

button.frame.size = image.size * 2

If you like the above idea, check out CGOperators, which contains math operator overloads for all Core Graphics' vector types.

26 Using closure types in generic constraints

🔗 You can use closure types in generic constraints in Swift. Enables nice APIs for handling sequences of closures.

extension Sequence where Element == () -> Void {
    func callAll() {
        forEach { $0() }
    }
}

extension Sequence where Element == () -> String {
    func joinedResults(separator: String) -> String {
        return map { $0() }.joined(separator: separator)
    }
}

callbacks.callAll()
let names = nameProviders.joinedResults(separator: ", ")

(If you're using Swift 3, you have to change Element to Iterator.Element)

25 Using associated enum values to avoid state-specific optionals

🎉 Using associated enum values is a super nice way to encapsulate mutually exclusive state info (and avoiding state-specific optionals).

// BEFORE: Lots of state-specific, optional properties

class Player {
    var isWaitingForMatchMaking: Bool
    var invitingUser: User?
    var numberOfLives: Int
    var playerDefeatedBy: Player?
    var roundDefeatedIn: Int?
}

// AFTER: All state-specific information is encapsulated in enum cases

class Player {
    enum State {
        case waitingForMatchMaking
        case waitingForInviteResponse(from: User)
        case active(numberOfLives: Int)
        case defeated(by: Player, roundNumber: Int)
    }
    
    var state: State
}

24 Using enums for async result types

👍 I really like using enums for all async result types, even boolean ones. Self-documenting, and makes the call site a lot nicer to read too!

protocol PushNotificationService {
    // Before
    func enablePushNotifications(completionHandler: @escaping (Bool) -> Void)
    
    // After
    func enablePushNotifications(completionHandler: @escaping (PushNotificationStatus) -> Void)
}

enum PushNotificationStatus {
    case enabled
    case disabled
}

service.enablePushNotifications { status in
    if status == .enabled {
        enableNotificationsButton.removeFromSuperview()
    }
}

23 Working on async code in a playground

🏃 Want to work on your async code in a Swift Playground? Just set needsIndefiniteExecution to true to keep it running:

import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
    let greeting = "Hello after 3 seconds"
    print(greeting)
}

To stop the playground from executing, simply call PlaygroundPage.current.finishExecution().

22 Overriding self with a weak reference

💦 Avoid memory leaks when accidentially refering to self in closures by overriding it locally with a weak reference:

Swift >= 4.2

dataLoader.loadData(from: url) { [weak self] result in
    guard let self = self else { 
        return 
    }

    self.cache(result)
    
    ...

Swift < 4.2

dataLoader.loadData(from: url) { [weak self] result in
    guard let `self` = self else {
        return
    }

    self.cache(result)
    
    ...

Note that the reason the above currently works is because of a compiler bug (which I hope gets turned into a properly supported feature soon).

21 Using DispatchWorkItem

🕓 Using dispatch work items you can easily cancel a delayed asynchronous GCD task if you no longer need it:

let workItem = DispatchWorkItem {
    // Your async code goes in here
}

// Execute the work item after 1 second
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: workItem)

// You can cancel the work item if you no longer need it
workItem.cancel()

20 Combining a sequence of functions

➕ While working on a new Swift developer tool (to be open sourced soon 😉), I came up with a pretty neat way of organizing its sequence of operations, by combining their functions into a closure:

internal func +<A, B, C>(lhs: @escaping (A) throws -> B,
                         rhs: @escaping (B) throws -> C) -> (A) throws -> C {
    return { try rhs(lhs($0)) }
}

public func run() throws {
    try (determineTarget + build + analyze + output)()
}

If you're familiar with the functional programming world, you might know the above technique as the pipe operator (thanks to Alexey Demedreckiy for pointing this out!)

19 Chaining optionals with map() and flatMap()

🗺 Using map() and flatMap() on optionals you can chain multiple operations without having to use lengthy if lets or guards:

// BEFORE

guard let string = argument(at: 1) else {
    return
}

guard let url = URL(string: string) else {
    return
}

handle(url)

// AFTER

argument(at: 1).flatMap(URL.init).map(handle)

18 Using self-executing closures for lazy properties

🚀 Using self-executing closures is a great way to encapsulate lazy property initialization:

class StoreViewController: UIViewController {
    private lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        let view = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
        view.delegate = self
        view.dataSource = self
        return view
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)
    }
}

17 Speeding up Swift package tests

⚡️ You can speed up your Swift package tests using the --parallel flag. For Marathon, the tests execute 3 times faster that way!

swift test --parallel

16 Avoiding mocking UserDefaults

🛠 Struggling with mocking UserDefaults in a test? The good news is: you don't need mocking - just create a real instance:

class LoginTests: XCTestCase {
    private var userDefaults: UserDefaults!
    private var manager: LoginManager!
    
    override func setUp() {
        super.setup()
        
        userDefaults = UserDefaults(suiteName: #file)
        userDefaults.removePersistentDomain(forName: #file)
        
        manager = LoginManager(userDefaults: userDefaults)
    }
}

15 Using variadic parameters

👍 Using variadic parameters in Swift, you can create some really nice APIs that take a list of objects without having to use an array:

extension Canvas {
    func add(_ shapes: Shape...) {
        shapes.forEach(add)
    }
}

let circle = Circle(center: CGPoint(x: 5, y: 5), radius: 5)
let lineA = Line(start: .zero, end: CGPoint(x: 10, y: 10))
let lineB = Line(start: CGPoint(x: 0, y: 10), end: CGPoint(x: 10, y: 0))

let canvas = Canvas()
canvas.add(circle, lineA, lineB)
canvas.render()

14 Referring to enum cases with associated values as closures

😮 Just like you can refer to a Swift function as a closure, you can do the same thing with enum cases with associated values:

enum UnboxPath {
    case key(String)
    case keyPath(String)
}

struct UserSchema {
    static let name = key("name")
    static let age = key("age")
    static let posts = key("posts")
    
    private static let key = UnboxPath.key
}

13 Using the === operator to compare objects by instance

📈 The === operator lets you check if two objects are the same instance. Very useful when verifying that an array contains an instance in a test:

protocol InstanceEquatable: class, Equatable {}

extension InstanceEquatable {
    static func ==(lhs: Self, rhs: Self) -> Bool {
        return lhs === rhs
    }
}

extension Enemy: InstanceEquatable {}

func testDestroyingEnemy() {
    player.attack(enemy)
    XCTAssertTrue(player.destroyedEnemies.contains(enemy))
}

12 Calling initializers with dot syntax and passing them as closures

😎 Cool thing about Swift initializers: you can call them using dot syntax and pass them as closures! Perfect for mocking dates in tests.

class Logger {
    private let storage: LogStorage
    private let dateProvider: () -> Date
    
    init(storage: LogStorage = .init(), dateProvider: @escaping () -> Date = Date.init) {
        self.storage = storage
        self.dateProvider = dateProvider
    }
    
    func log(event: Event) {
        storage.store(event: event, date: dateProvider())
    }
}

11 Structuring UI tests as extensions on XCUIApplication

📱 Most of my UI testing logic is now categories on XCUIApplication. Makes the test cases really easy to read:

func testLoggingInAndOut() {
    XCTAssertFalse(app.userIsLoggedIn)
    
    app.launch()
    app.login()
    XCTAssertTrue(app.userIsLoggedIn)
    
    app.logout()
    XCTAssertFalse(app.userIsLoggedIn)
}

func testDisplayingCategories() {
    XCTAssertFalse(app.isDisplayingCategories)
    
    app.launch()
    app.login()
    app.goToCategories()
    XCTAssertTrue(app.isDisplayingCategories)
}

10 Avoiding default cases in switch statements

🙂 It’s a good idea to avoid “default” cases when switching on Swift enums - it’ll “force you” to update your logic when a new case is added:

enum State {
    case loggedIn
    case loggedOut
    case onboarding
}

func handle(_ state: State) {
    switch state {
    case .loggedIn:
        showMainUI()
    case .loggedOut:
        showLoginUI()
    // Compiler error: Switch must be exhaustive
    }
}

9 Using the guard statement in many different scopes

💂 It's really cool that you can use Swift's 'guard' statement to exit out of pretty much any scope, not only return from functions:

// You can use the 'guard' statement to...

for string in strings {
    // ...continue an iteration
    guard shouldProcess(string) else {
        continue
    }
    
    // ...or break it
    guard !shouldBreak(for: string) else {
        break
    }
    
    // ...or return
    guard !shouldReturn(for: string) else {
        return
    }
    
    // ..or throw an error
    guard string.isValid else {
        throw StringError.invalid(string)
    }
    
    // ...or exit the program
    guard !shouldExit(for: string) else {
        exit(1)
    }
}

8 Passing functions & operators as closures

❤️ Love how you can pass functions & operators as closures in Swift. For example, it makes the syntax for sorting arrays really nice!

let array = [3, 9, 1, 4, 6, 2]
let sorted = array.sorted(by: <)

7 Using #function for UserDefaults key consistency

🗝 Here's a neat little trick I use to get UserDefault key consistency in Swift (#function expands to the property name in getters/setters). Just remember to write a good suite of tests that'll guard you against bugs when changing property names.

extension UserDefaults {
    var onboardingCompleted: Bool {
        get { return bool(forKey: #function) }
        set { set(newValue, forKey: #function) }
    }
}

6 Using a name already taken by the standard library

📛 Want to use a name already taken by the standard library for a nested type? No problem - just use Swift. to disambiguate:

extension Command {
    enum Error: Swift.Error {
        case missing
        case invalid(String)
    }
}

5 Using Wrap to implement Equatable

📦 Playing around with using Wrap to implement Equatable for any type, primarily for testing:

protocol AutoEquatable: Equatable {}

extension AutoEquatable {
    static func ==(lhs: Self, rhs: Self) -> Bool {
        let lhsData = try! wrap(lhs) as Data
        let rhsData = try! wrap(rhs) as Data
        return lhsData == rhsData
    }
}

4 Using typealiases to reduce the length of method signatures

📏 One thing that I find really useful in Swift is to use typealiases to reduce the length of method signatures in generic types:

public class PathFinder<Object: PathFinderObject> {
    public typealias Map = Object.Map
    public typealias Node = Map.Node
    public typealias Path = PathFinderPath<Object>
    
    public static func possiblePaths(for object: Object, at rootNode: Node, on map: Map) -> Path.Sequence {
        return .init(object: object, rootNode: rootNode, map: map)
    }
}

3 Referencing either external or internal parameter name when writing docs

📖 You can reference either the external or internal parameter label when writing Swift docs - and they get parsed the same:

// EITHER:

class Foo {
    /**
    *   - parameter string: A string
    */
    func bar(with string: String) {}
}

// OR:

class Foo {
    /**
    *   - parameter with: A string
    */
    func bar(with string: String) {}
}

2 Using auto closures

👍 Finding more and more uses for auto closures in Swift. Can enable some pretty nice APIs:

extension Dictionary {
    mutating func value(for key: Key, orAdd valueClosure: @autoclosure () -> Value) -> Value {
        if let value = self[key] {
            return value
        }
        
        let value = valueClosure()
        self[key] = value
        return value
    }
}

1 Namespacing with nested types

🚀 I’ve started to become a really big fan of nested types in Swift. Love the additional namespacing it gives you!

public struct Map {
    public struct Model {
        public let size: Size
        public let theme: Theme
        public var terrain: [Position : Terrain.Model]
        public var units: [Position : Unit.Model]
        public var buildings: [Position : Building.Model]
    }
    
    public enum Direction {
        case up
        case right
        case down
        case left
    }
    
    public struct Position {
        public var x: Int
        public var y: Int
    }
    
    public enum Size: String {
        case small = "S"
        case medium = "M"
        case large = "L"
        case extraLarge = "XL"
    }
}

Download Details:

Author: JohnSundell
Source Code: https://github.com/JohnSundell/SwiftTips 
License: MIT license

#swift #tips #tricks 

A Collection Of Swift Tips & Tricks That I've Shared on Twitter
harvey h

harvey h

1663914710

Top Handy Tips & Tools To Boost Mobile App Performance

Due to their ever-increasing popularity, #mobileapp has begun taking over large shares of global online traffic. Feature-rich. User-friendly interface. Engaging content. These are great selling points for a #mobile #app.

Although it may not be what convinces people to download your app, #performance can be a huge reason why they continue to engage with it—or simply uninstall it.

Check out the blog to know about the best #tips and tools for boosting #mobileappperformance. Click👇

https://aglowiditsolutions.com/blog/tips-to-boost-mobile-app-performance/

 

Top Handy Tips & Tools To Boost Mobile App Performance

✨Stop Memorizing Code! Do This:✨

Honestly, if you are a beginner and trying to learn code by memorizing, you are doomed to fail. There's a lot to memorize, don't overload your Hippocampus...

#python #codenewbies #code #tips #javascript

Do this 👇✔

https://blog.octachart.com/stop-memorizing-code-do-this-instead

✨Stop Memorizing Code! Do This:✨
Hans  Marvin

Hans Marvin

1659806940

Top 10 Tips and Tricks Related To The Quarkus Framework

In this article, you will learn some useful tips and tricks related to the Quarkus framework. We will focus on the features that stand Quarkus out from the other frameworks

If you run your applications on Kubernetes, Quarkus is obviously a good choice. It starts fast and does not consume much memory. You may easily compile it natively with GraalVM. It provides a lot of useful developers features like e.g. hot reload. I hope you will find there tips and techniques that help to boost your productivity in Quarkus development. Or maybe just convince you to take a look at it, if don’t have any experience yet.

See more at: https://piotrminkowski.com/2021/10/12/quarkus-tips-tricks-and-techniques/

#quarkus #java #tips

Top 10 Tips and Tricks Related To The Quarkus Framework
Thierry  Perret

Thierry Perret

1659799680

Top 10 Des Trucs Et Astuces Liés Au Framework Quarkus

Dans cet article, vous apprendrez quelques trucs et astuces utiles liés au framework Quarkus. Nous nous concentrerons sur les fonctionnalités qui distinguent Quarkus des autres frameworks

Si vous exécutez vos applications sur Kubernetes, Quarkus est évidemment un bon choix. Il démarre rapidement et ne consomme pas beaucoup de mémoire. Vous pouvez facilement le compiler nativement avec GraalVM. Il fournit de nombreuses fonctionnalités utiles aux développeurs, comme par exemple le rechargement à chaud. J'espère que vous y trouverez des conseils et des techniques qui vous aideront à augmenter votre productivité dans le développement Quarkus. Ou peut-être simplement vous convaincre d'y jeter un coup d'œil, si vous n'avez pas encore d'expérience.

Table des matières

  • Astuce 1. Utilisez l'outil de ligne de commande Quarkus
  • Astuce 2. Utilisez les services de développement avec des bases de données
  • Astuce 3. Utilisez un ORM simplifié avec Panache
  • Astuce 4. Configuration unifiée en option
  • Astuce 5. Déployer sur Kubernetes avec Maven
  • Astuce 6. Accéder à la console Dev UI
  • Astuce 7. Testez en continu
  • Astuce 8. Compiler nativement avec GraalVM sur OpenShift
  • Astuce 9. Rollback transaction après chaque test
  • Astuce 10. Profitez du support de GraphQL
  • Réflexions finales

Astuce 1. Utilisez l'outil de ligne de commande Quarkus

Comment démarrez-vous une nouvelle application lorsque vous utilisez l'un des frameworks Java populaires ? Vous pouvez vous rendre sur le site Web du générateur en ligne, qui est généralement fourni par ces frameworks. Avez-vous entendu parler de Spring Initializr ? Quarkus propose un site similaire disponible à l' adresse https://code.quarkus.io/ . Mais ce que vous ne savez peut-être pas, il y a aussi l'outil Quarkus CLI. Il vous permet de créer des projets, de gérer des extensions et d'exécuter des commandes de construction et de développement. Par exemple, vous pouvez créer un code source pour une nouvelle application à l'aide d'une seule commande, comme indiqué ci-dessous.

$ quarkus create app --package-name=pl.piomin.samples.quarkus \
  -x resteasy-jackson,hibernate-orm-panache,jdbc-postgresql \
  -o person-service \
  pl.piomin.samples:person-service

Après avoir exécuté la commande visible ci-dessus vous devriez avoir un écran similaire.

quarkus-conseils-cli

Cette commande crée une application REST simple qui utilise la base de données PostgreSQL et la couche ORM Quarkus. En outre, il définit le nom de l'application, Maven groupIdet artifactId. Après cela, vous pouvez simplement exécuter l'application. Pour ce faire, accédez au répertoire généré et exécutez la commande suivante. Alternativement, vous pouvez exécuter la mvn quarkus:devcommande.

$ quarkus dev

L'application ne démarre pas correctement car aucune connexion à la base de données n'est configurée. Est-ce qu'on est obligé de faire ça ? Non! Passons à la section suivante pour voir pourquoi.

Astuce 2. Utilisez les services de développement avec des bases de données

Avez-vous entendu parler de Testcontainers ? Il s'agit d'une bibliothèque Java qui vous permet d'exécuter automatiquement des conteneurs lors de tests JUnit. Vous pouvez exécuter des bases de données communes, des navigateurs Web Selenium ou tout autre élément pouvant s'exécuter dans un conteneur Docker. Quarkus fournit une intégration intégrée avec Testcontainers lors de l'exécution d'applications en mode développement ou test. Cette fonctionnalité s'appelle Dev Services. De plus, vous n'avez rien à faire pour l'activer. NE PAS FOURNIR simplement l'URL de connexion et les informations d'identification.

Revenons à notre scénario. Nous avons déjà créé l'application à l'aide de Quarkus CLI. Il contient toutes les bibliothèques nécessaires. Donc, la seule chose que nous devons faire maintenant est d'exécuter un démon Docker. Grâce à cela, Quarkus essaiera d'exécuter PostgreSQL avec Testcontainers en mode développement. Quel est le résultat final ? Notre application fonctionne et est connectée à PostgreSQL démarré avec Docker comme indiqué ci-dessous.

Ensuite, nous pouvons procéder au développement. Avec la quarkus devcommande, nous avons déjà activé le mode de développement. Grâce à cela, nous pouvons profiter de la fonction de rechargement en direct.

Astuce 3. Utilisez un ORM simplifié avec Panache

Ajoutons du code à notre exemple d'application. Nous allons implémenter une couche de données en utilisant Quarkus Panache ORM. C'est un module très intéressant qui vise à rendre vos entités triviales et amusantes à écrire dans Quarkus. Voici notre classe d'entités. Grâce à la réécriture d'accès aux champs de Quarkus, lorsque vous lisez,  person.name vous appelez en fait votre  getName() accesseur, et de même pour les écritures de champs et le setter. Cela permet une encapsulation correcte au moment de l'exécution car tous les appels de champs seront remplacés par les appels getter ou setter correspondants. Le PanacheEntityprend également en charge la mise en œuvre de la clé primaire.

@Entity
public class Person extends PanacheEntity {
   public String name;
   public int age;
   @Enumerated(EnumType.STRING)
   public Gender gender;
}

Dans l'étape suivante, nous allons définir la classe de référentiel. Puisqu'il implémente l' PanacheRepositoryinterface, nous n'avons qu'à ajouter nos méthodes de recherche personnalisées.

@ApplicationScoped
public class PersonRepository implements PanacheRepository<Person> {

    public List<Person> findByName(String name) {
        return find("name", name).list();
    }

    public List<Person> findByAgeGreaterThan(int age) {
        return find("age > ?1", age).list();
    }
}

Enfin, ajoutons une classe de ressources avec des points de terminaison REST.

@Path("/persons")
public class PersonResource {

    @Inject
    PersonRepository personRepository;

    @POST
    @Transactional
    public Person addPerson(Person person) {
        personRepository.persist(person);
        return person;
    }

    @GET
    public List<Person> getPersons() {
        return personRepository.listAll();
    }

    @GET
    @Path("/name/{name}")
    public List<Person> getPersonsByName(@PathParam("name") String name) {
        return personRepository.findByName(name);
    }

    @GET
    @Path("/age-greater-than/{age}")
    public List<Person> getPersonsByName(@PathParam("age") int age) {
        return personRepository.findByAgeGreaterThan(age);
    }

    @GET
    @Path("/{id}")
    public Person getPersonById(@PathParam("id") Long id) {
        return personRepository.findById(id);
    }

}

Aussi, créons le import.sqlfichier dans le src/main/resourcesrépertoire. Il charge les instructions SQL au démarrage d'Hibernate ORM.

insert into person(id, name, age, gender) values(1, 'John Smith', 25, 'MALE');
insert into person(id, name, age, gender) values(2, 'Paul Walker', 65, 'MALE');
insert into person(id, name, age, gender) values(3, 'Lewis Hamilton', 35, 'MALE');
insert into person(id, name, age, gender) values(4, 'Veronica Jones', 20, 'FEMALE');
insert into person(id, name, age, gender) values(5, 'Anne Brown', 60, 'FEMALE');
insert into person(id, name, age, gender) values(6, 'Felicia Scott', 45, 'FEMALE');

Enfin, nous pouvons appeler notre point de terminaison REST.

$ curl http://localhost:8080/persons

 

Astuce 4. Configuration unifiée en option

En supposant que nous ne voulons pas exécuter une base de données sur Docker, nous devons configurer la connexion dans application.properties. Par défaut, Quarkus propose 3 profils : prod, test, dev. Nous pouvons définir des propriétés pour plusieurs profils à l'intérieur d'un seul application.propertiesen utilisant la syntaxe  %{profile-name}.config.name. Dans notre cas, il y a une instance H2 utilisée dans les modes devet testet une instance PostgreSQL externe dans le prodmode.

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${POSTGRES_USER}
quarkus.datasource.password=${POSTGRES_PASSWORD}
quarkus.datasource.jdbc.url=jdbc:postgresql://person-db:5432/${POSTGRES_DB}

%test.quarkus.datasource.db-kind=h2
%test.quarkus.datasource.username=sa
%test.quarkus.datasource.password=password
%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb

%dev.quarkus.datasource.db-kind=h2
%dev.quarkus.datasource.username=sa
%dev.quarkus.datasource.password=password
%dev.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb

Avant d'exécuter une nouvelle version de l'application, nous devons inclure la dépendance H2 dans Maven pom.xml.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-jdbc-h2</artifactId>
</dependency>

Vous pouvez également définir votre profil personnalisé et fournir des propriétés en l'utilisant comme préfixe. Bien sûr, vous pouvez toujours définir des fichiers spécifiques au profil comme application-{profile}.properties.

Astuce 5. Déployer sur Kubernetes avec Maven

Quarkus dans un framework natif Kubernetes. Vous pouvez facilement déployer votre application Quarkus sur le cluster Kubernetes sans créer de fichiers YAML manuellement. Pour des configurations plus avancées comme par exemple le mappage de secrets à des variables d'environnement, vous pouvez utiliser application.properties. D'autres choses comme par exemple les bilans de santé sont détectées dans le code source. Pour activer cela, nous devons inclure le quarkus-kubernetesmodule. Il existe également une implémentation pour OpenShift.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-openshift</artifactId>
</dependency>

Après cela, Quarkus générera des manifestes de déploiement lors de la construction Maven. Nous pouvons activer le déploiement automatique sur le cluster Kubernetes actuel en définissant la propriété quarkus.kubernetes.deploysur true. Pour le déploiement OpenShift, nous devons changer la cible de déploiement par défaut de kubernetesà openshift.

quarkus.container-image.build = true
quarkus.kubernetes.deploy = true
quarkus.kubernetes.deployment-target = openshift

Supposons que nous ayons une configuration personnalisée à définir sur le Deploymentmanifeste. Notre application s'exécutera dans deux pods et sera automatiquement exposée en dehors du cluster. Il injecte également des valeurs de Secretafin de se connecter à la base de données PostgreSQL.

quarkus.openshift.expose = true
quarkus.openshift.replicas = 2
quarkus.openshift.labels.app = person-app
quarkus.openshift.annotations.app-type = demo
quarkus.openshift.env.mapping.postgres_user.from-secret = person-db
quarkus.openshift.env.mapping.postgres_user.with-key = database-user
quarkus.openshift.env.mapping.postgres_password.from-secret = person-db
quarkus.openshift.env.mapping.postgres_password.with-key = database-password
quarkus.openshift.env.mapping.postgres_db.from-secret = person-db
quarkus.openshift.env.mapping.postgres_db.with-key = database-name

Ensuite, nous avons juste besoin de construire notre application avec Maven. Alternativement, nous pouvons supprimer la quarkus.kubernetes.deploypropriété de application.propertieset l'activer sur la commande Maven.

$ maven clean package -D<meta charset="utf-8">quarkus.kubernetes.deploy=true

 

Astuce 6. Accéder à la console Dev UI

Après avoir exécuté l'application Quarkus en mode dev ( mvn<em> </em>quarkus:dev), vous pouvez accéder à la console Dev UI sous l'adresse http://localhost:8080/q/dev . Plus vous incluez de modules, plus vous pouvez y configurer d'options. L'une de mes fonctionnalités préférées ici est la possibilité de déployer des applications sur OpenShift. Au lieu d'exécuter la commande Maven pour créer une application, nous pouvons simplement l'exécuter en devmode et la déployer à l'aide de l'interface utilisateur graphique.

quarkus-tips-dev-ui

 

Astuce 7. Testez en continu

Quarkus prend en charge les tests continus, où les tests s'exécutent immédiatement après les modifications de code. Cela vous permet d'obtenir un retour instantané sur vos modifications de code. Quarkus détecte quels tests couvrent quel code et utilise ces informations pour n'exécuter les tests pertinents que lorsque le code est modifié. Après avoir exécuté l'application en cours de développement, vous serez invité à activer cette fonctionnalité, comme indiqué ci-dessous. Appuyez simplement sur rpour l'activer.

Ok, alors ajoutons quelques tests pour notre exemple d'application. Tout d'abord, nous devons inclure le module Quarkus Test et la bibliothèque REST Assured.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-junit5</artifactId>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>io.rest-assured</groupId>
   <artifactId>rest-assured</artifactId>
   <scope>test</scope>
</dependency>

Ensuite, nous ajouterons quelques tests d'API simples. La classe de test doit être annotée avec @QuarkusTest. Le reste de l'implémentation est typique de la bibliothèque REST Assured.

@QuarkusTest
public class PersonResourceTests {

    @Test
    void getPersons() {
        List<Person> persons = given().when().get("/persons")
                .then()
                .statusCode(200)
                .extract()
                .body()
                .jsonPath().getList(".", Person.class);
        assertEquals(persons.size(), 6);
    }

    @Test
    void getPersonById() {
        Person person = given()
                .pathParam("id", 1)
                .when().get("/persons/{id}")
                .then()
                .statusCode(200)
                .extract()
                .body().as(Person.class);
        assertNotNull(person);
        assertEquals(1L, person.id);
    }

    @Test
    void newPersonAdd() {
        Person newPerson = new Person();
        newPerson.age = 22;
        newPerson.name = "TestNew";
        newPerson.gender = Gender.FEMALE;
        Person person = given()
                .body(newPerson)
                .contentType(ContentType.JSON)
                .when().post("/persons")
                .then()
                .statusCode(200)
                .extract()
                .body().as(Person.class);
        assertNotNull(person);
        assertNotNull(person.id);
    }
}

Nous pouvons également exécuter ces tests JUnit à partir de la console Dev UI. Tout d'abord, vous devez vous rendre sur la console Dev UI. En bas de page, vous trouverez le module de test du responsable du panel. Cliquez simplement sur l' icône Résultat du test et vous verrez un écran similaire à celui visible ci-dessous.

 

Astuce 8. Compiler nativement avec GraalVM sur OpenShift

Vous pouvez facilement créer et exécuter une image Quarkus GraalVM native sur OpenShift à l'aide d'une seule commande et d'un ubi-quarkus-native-s2igénérateur. OpenShift construit l'application en utilisant l'approche S2I (source-2-image). Bien sûr, vous avez juste besoin d'un cluster OpenShift en cours d'exécution (par exemple, CRC local ou Developer Sandbox https://developers.redhat.com/products/codeready-containers/overview ) et le occlient installé localement.

$ oc new-app --name person-native \
             --context-dir basic-with-db/person-app \
  quay.io/quarkus/ubi-quarkus-native-s2i:21.2-java11~https://github.com/piomin/openshift-quickstart.git

Astuce 9. Rollback transaction après chaque test

Si vous devez annuler les modifications apportées aux données après chaque test, évitez de le faire manuellement. Au lieu de cela, il vous suffit d'annoter votre classe de test avec @TestTransaction. La restauration est effectuée chaque fois que la méthode de test est terminée.

@QuarkusTest
@TestTransaction
public class PersonRepositoryTests {

    @Inject
    PersonRepository personRepository;

    @Test
    void addPerson() {
        Person newPerson = new Person();
        newPerson.age = 22;
        newPerson.name = "TestNew";
        newPerson.gender = Gender.FEMALE;
        personRepository.persist(newPerson);
        Assertions.assertNotNull(newPerson.id);
    }
}

 

Astuce 10. Profitez du support de GraphQL

C'est le dernier conseil Quarkus de cet article. Cependant, c'est l'une de mes fonctionnalités Quarkus préférées. La prise en charge de GraphQL n'est pas un côté fort de Spring Boot. D'autre part, Quarkus fournit des extensions très sympas et simples pour GraphQL côté client et côté serveur.

Tout d'abord, ajoutons les modules Quarkus responsables du support de GraphQL.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-graphql</artifactId>
</dependency>
<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-graphql-client</artifactId>
   <scope>test</scope>
</dependency>

Ensuite, nous pouvons créer un code responsable de l'exposition de l'API GraphQL. La classe doit être annotée avec @GraphQLAPI. Quarkus génère automatiquement le schéma GraphQL à partir du code source.

@GraphQLApi
public class EmployeeFetcher {

    private EmployeeRepository repository;

    public EmployeeFetcher(EmployeeRepository repository){
        this.repository = repository;
    }

    @Query("employees")
    public List<Employee> findAll() {
        return repository.listAll();
    }

    @Query("employee")
    public Employee findById(@Name("id") Long id) {
        return repository.findById(id);
    }

    @Query("employeesWithFilter")
    public List<Employee> findWithFilter(@Name("filter") EmployeeFilter filter) {
        return repository.findByCriteria(filter);
    }

}

Ensuite, créons une interface client pour appeler deux points de terminaison. Nous devons annoter cette interface avec @GraphQLClientApi.

@GraphQLClientApi(configKey = "employee-client")
public interface EmployeeClient {

    List<Employee> employees();
    Employee employee(Long id);
}

Enfin, nous pouvons ajouter un simple test JUnit. Nous avons juste besoin d'injecter EmployeeClient, puis d'appeler des méthodes. Si vous souhaitez plus de détails sur la prise en charge de Quarkus GraphQL, lisez mon article An Advanced GraphQL with Quarkus .

@QuarkusTest
public class EmployeeFetcherTests {

    @Inject
    EmployeeClient employeeClient;

    @Test
    void fetchAll() {
        List<Employee> employees = employeeClient.employees();
        Assertions.assertEquals(10, employees.size());
    }

    @Test
    void fetchById() {
        Employee employee = employeeClient.employee(10L);
        Assertions.assertNotNull(employee);
    }
}

Réflexions finales

À mon avis, Quarkus est un framework très intéressant et prometteur. Grâce à ces conseils, vous pouvez facilement démarrer le développement de votre première application avec Quarkus. Il y a quelques nouvelles fonctionnalités intéressantes dans chaque nouvelle version. Alors peut-être que je devrai bientôt mettre à jour cette liste de conseils Quarkus

Lien : https://piotrminkowski.com/2021/10/12/quarkus-tips-tricks-and-techniques/

#quarkus #java #tips

Top 10 Des Trucs Et Astuces Liés Au Framework Quarkus
田辺  亮介

田辺 亮介

1659788760

與 Quarkus 框架相關的 10 大技巧和竅門

在本文中,您將學習一些與 Quarkus 框架相關的有用提示和技巧。我們將專注於讓 Quarkus 從其他框架中脫穎而出的特性

如果你在 Kubernetes 上運行你的應用程序,Quarkus 顯然是一個不錯的選擇。它啟動速度很快,不會消耗太多內存。您可以使用 GraalVM 輕鬆地在本地編譯它。它提供了許多有用的開發人員功能,例如熱重載。我希望您能找到有助於提高 Quarkus 開發效率的技巧和技術。或者也許只是說服你看看它,如果還沒有任何經驗的話。

目錄

  • 提示 1. 使用 Quarkus 命令行工具
  • 提示 2. 將開發服務與數據庫一起使用
  • 提示 3. 使用帶有 Panache 的簡化 ORM
  • 提示 4. 統一配置作為選項
  • 技巧 5. 使用 Maven 部署到 Kubernetes
  • 提示 6. 訪問 Dev UI 控制台
  • 提示 7. 持續測試
  • 技巧 8. 在 OpenShift 上使用 GraalVM 進行本機編譯
  • 技巧 9. 每次測試後回滾事務
  • 提示 10. 利用 GraphQL 支持
  • 最後的想法

提示 1. 使用 Quarkus 命令行工具

使用流行的 Java 框架之一時如何啟動新應用程序?您可以訪問在線生成器網站,該網站通常由那些框架提供。你聽說過 Spring Initializr 嗎?Quarkus 在https://code.quarkus.io/上提供了一個類似的站點。但你可能不知道,還有 Quarkus CLI 工具。它允許您創建項目、管理擴展以及執行構建和開發命令。例如,您可以使用如下所示的單個命令為新應用程序創建源代碼。

$ quarkus create app --package-name=pl.piomin.samples.quarkus \
  -x resteasy-jackson,hibernate-orm-panache,jdbc-postgresql \
  -o person-service \
  pl.piomin.samples:person-service

執行上面可見的命令後,您應該會看到類似的屏幕。

quarkus-提示-cli

此命令創建一個使用 PostgreSQL 數據庫和 Quarkus ORM 層的簡單 REST 應用程序。此外,它設置應用程序的名稱 MavengroupIdartifactId. 之後,您可以運行該應用程序。為此,請轉到生成的目錄並運行以下命令。或者,您可以執行mvn quarkus:dev命令。

$ quarkus dev

應用程序沒有成功啟動,因為沒有配置數據庫連接。我們必須這樣做嗎?不!讓我們繼續下一節看看為什麼。

提示 2. 將開發服務與數據庫一起使用

你聽說過 Testcontainers 嗎?它是一個 Java 庫,允許您在 JUnit 測試期間自動運行容器。您可以運行通用數據庫、Selenium Web 瀏覽器或任何其他可以在 Docker 容器中運行的東西。在開發或測試模式下運行應用程序時,Quarkus 提供與 Testcontainers 的內置集成。此功能稱為開發服務。此外,您無需執行任何操作即可啟用它。只是不要提供連接 URL 和憑據。

讓我們回到我們的場景。我們已經使用 Quarkus CLI 創建了應用程序。它包含所有必需的庫。所以,我們現在唯一需要做的就是運行一個 Docker 守護進程。感謝 Quarkus 將嘗試在開發模式下使用 Testcontainers 運行 PostgreSQL。最後的結果是什麼?我們的應用程序正在運行,它與使用 Docker 啟動的 PostgreSQL 連接,如下所示。

然後,我們可以繼續開發。使用該quarkus dev命令,我們已經啟用了開發模式。多虧了這一點,我們可以利用實時重新加載功能。

提示 3. 使用帶有 Panache 的簡化 ORM

讓我們在示例應用程序中添加一些代碼。我們將使用 Quarkus Panache ORM 實現數據層。這是一個非常有趣的模塊,它專注於讓你的實體在 Quarkus 中編寫變得簡單而有趣。這是我們的實體類。感謝 Quarkus 字段訪問重寫,當您閱讀時, person.name 您實際上會調用您的 getName() 訪問器,對於字段寫入和設置器也是如此。這允許在運行時進行適當的封裝,因為所有字段調用都將被相應的 getter 或 setter 調用替換。PanacheEntity還負責主鍵的實現。

@Entity
public class Person extends PanacheEntity {
   public String name;
   public int age;
   @Enumerated(EnumType.STRING)
   public Gender gender;
}

在下一步中,我們將定義存儲庫類。由於它實現了PanacheRepository接口,我們只需要添加我們自定義的查找方法。

@ApplicationScoped
public class PersonRepository implements PanacheRepository<Person> {

    public List<Person> findByName(String name) {
        return find("name", name).list();
    }

    public List<Person> findByAgeGreaterThan(int age) {
        return find("age > ?1", age).list();
    }
}

最後,讓我們添加一個帶有 REST 端點的資源類。

@Path("/persons")
public class PersonResource {

    @Inject
    PersonRepository personRepository;

    @POST
    @Transactional
    public Person addPerson(Person person) {
        personRepository.persist(person);
        return person;
    }

    @GET
    public List<Person> getPersons() {
        return personRepository.listAll();
    }

    @GET
    @Path("/name/{name}")
    public List<Person> getPersonsByName(@PathParam("name") String name) {
        return personRepository.findByName(name);
    }

    @GET
    @Path("/age-greater-than/{age}")
    public List<Person> getPersonsByName(@PathParam("age") int age) {
        return personRepository.findByAgeGreaterThan(age);
    }

    @GET
    @Path("/{id}")
    public Person getPersonById(@PathParam("id") Long id) {
        return personRepository.findById(id);
    }

}

另外,讓我們import.sql在目錄中創建文件src/main/resources。它在 Hibernate ORM 啟動時加載 SQL 語句。

insert into person(id, name, age, gender) values(1, 'John Smith', 25, 'MALE');
insert into person(id, name, age, gender) values(2, 'Paul Walker', 65, 'MALE');
insert into person(id, name, age, gender) values(3, 'Lewis Hamilton', 35, 'MALE');
insert into person(id, name, age, gender) values(4, 'Veronica Jones', 20, 'FEMALE');
insert into person(id, name, age, gender) values(5, 'Anne Brown', 60, 'FEMALE');
insert into person(id, name, age, gender) values(6, 'Felicia Scott', 45, 'FEMALE');

最後,我們可以調用我們的 REST 端點。

$ curl http://localhost:8080/persons

 

提示 4. 統一配置作為選項

假設我們不想在 Docker 上運行數據庫,我們應該在application.properties. 默認情況下,Quarkus 提供 3 個配置文件:prodtestdevapplication.properties我們可以使用語法  為單個配置文件中的多個配置文件定義屬性%{profile-name}.config.namedev在我們的例子中,在and模式中使用了一個 H2 實例,在模式中使用了test一個外部 PostgreSQL 實例prod

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${POSTGRES_USER}
quarkus.datasource.password=${POSTGRES_PASSWORD}
quarkus.datasource.jdbc.url=jdbc:postgresql://person-db:5432/${POSTGRES_DB}

%test.quarkus.datasource.db-kind=h2
%test.quarkus.datasource.username=sa
%test.quarkus.datasource.password=password
%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb

%dev.quarkus.datasource.db-kind=h2
%dev.quarkus.datasource.username=sa
%dev.quarkus.datasource.password=password
%dev.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb

在運行新版本應用程序之前,我們必須在 Maven 中包含 H2 依賴項pom.xml

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-jdbc-h2</artifactId>
</dependency>

您還可以定義自定義配置文件並使用它作為前綴提供屬性。當然,您仍然可以定義特定於配置文件的文件,例如application-{profile}.properties.

技巧 5. 使用 Maven 部署到 Kubernetes

Kubernetes 原生框架中的 Quarkus。您可以輕鬆地將 Quarkus 應用程序部署到 Kubernetes 集群,而無需手動創建任何 YAML 文件。對於更高級的配置,例如將機密映射到環境變量,您可以使用application.properties. 在源代碼中檢測到其他內容,例如健康檢查。要啟用此功能,我們需要包含該quarkus-kubernetes模塊。OpenShift 也有一個實現。

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-openshift</artifactId>
</dependency>

之後,Quarkus 將在 Maven 構建期間生成部署清單。quarkus.kubernetes.deploy我們可以通過將屬性設置為來啟用對當前 Kubernetes 集群的自動部署true。對於 OpenShift 部署,我們必須將默認部署目標從 更改kubernetesopenshift.

quarkus.container-image.build = true
quarkus.kubernetes.deploy = true
quarkus.kubernetes.deployment-target = openshift

假設我們在Deployment清單上設置了一些自定義配置。我們的應用程序將在兩個 pod 中運行,並自動暴露在集群之外。它還注入值Secret以連接 PostgreSQL 數據庫。

quarkus.openshift.expose = true
quarkus.openshift.replicas = 2
quarkus.openshift.labels.app = person-app
quarkus.openshift.annotations.app-type = demo
quarkus.openshift.env.mapping.postgres_user.from-secret = person-db
quarkus.openshift.env.mapping.postgres_user.with-key = database-user
quarkus.openshift.env.mapping.postgres_password.from-secret = person-db
quarkus.openshift.env.mapping.postgres_password.with-key = database-password
quarkus.openshift.env.mapping.postgres_db.from-secret = person-db
quarkus.openshift.env.mapping.postgres_db.with-key = database-name

然後我們只需要使用 Maven 構建我們的應用程序。或者,我們可以quarkus.kubernetes.deploy從 Maven 命令中刪除該屬性application.properties並啟用它。

$ maven clean package -D<meta charset="utf-8">quarkus.kubernetes.deploy=true

 

提示 6. 訪問 Dev UI 控制台

在開發模式 ( ) 下運行 Quarkus 應用程序後,您可以在地址http://localhost:8080/q/devmvn<em> </em>quarkus:dev下訪問 Dev UI 控制台。您包含的模塊越多,您可以在那裡配置的選項就越多。我最喜歡的功能之一是能夠將應用程序部署到 OpenShift。我們可以在模式下運行它並使用圖形 UI 進行部署,而不是運行 Maven 命令來構建應用程序。dev

quarkus-tips-dev-ui

 

提示 7. 持續測試

Quarkus 支持持續測試,在代碼更改後立即運行測試。這使您可以立即獲得有關代碼更改的反饋。Quarkus 檢測哪些測試覆蓋了哪些代碼,並使用此信息僅在代碼更改時運行相關測試。在開發中運行應用程序後,系統將提示您啟用該功能,如下所示。只需按下r即可啟用它。

好的,讓我們為我們的示例應用程序添加一些測試。首先,我們需要包含 Quarkus 測試模塊和 REST Assured 庫。

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-junit5</artifactId>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>io.rest-assured</groupId>
   <artifactId>rest-assured</artifactId>
   <scope>test</scope>
</dependency>

然後我們將添加一些簡單的 API 測試。測試類必須用@QuarkusTest. 其餘的實現對於 REST Assured 庫來說是典型的。

@QuarkusTest
public class PersonResourceTests {

    @Test
    void getPersons() {
        List<Person> persons = given().when().get("/persons")
                .then()
                .statusCode(200)
                .extract()
                .body()
                .jsonPath().getList(".", Person.class);
        assertEquals(persons.size(), 6);
    }

    @Test
    void getPersonById() {
        Person person = given()
                .pathParam("id", 1)
                .when().get("/persons/{id}")
                .then()
                .statusCode(200)
                .extract()
                .body().as(Person.class);
        assertNotNull(person);
        assertEquals(1L, person.id);
    }

    @Test
    void newPersonAdd() {
        Person newPerson = new Person();
        newPerson.age = 22;
        newPerson.name = "TestNew";
        newPerson.gender = Gender.FEMALE;
        Person person = given()
                .body(newPerson)
                .contentType(ContentType.JSON)
                .when().post("/persons")
                .then()
                .statusCode(200)
                .extract()
                .body().as(Person.class);
        assertNotNull(person);
        assertNotNull(person.id);
    }
}

我們也可以從 Dev UI 控制台運行這些 JUnit 測試。首先,您應該轉到 Dev UI 控制台。在頁面底部,您會找到面板負責測試模塊。只需單擊測試結果圖標,您將看到類似於下圖的屏幕。

 

技巧 8. 在 OpenShift 上使用 GraalVM 進行本機編譯

您可以使用單個命令和ubi-quarkus-native-s2i構建器在 OpenShift 上輕鬆構建和運行本機 Quarkus GraalVM 映像。OpenShift 使用 S2I(source-2-image)方法構建應用程序。當然,您只需要一個正在運行的 OpenShift 集群(例如本地 CRC 或 Developer Sandbox https://developers.redhat.com/products/codeready-containers/overview)和oc本地安裝的客戶端。

$ oc new-app --name person-native \
             --context-dir basic-with-db/person-app \
  quay.io/quarkus/ubi-quarkus-native-s2i:21.2-java11~https://github.com/piomin/openshift-quickstart.git

技巧 9. 每次測試後回滾事務

如果您需要在每次測試後回滾數據更改,請避免手動執行。相反,您只需要使用@TestTransaction. 每次測試方法完成時都會執行回滾。

@QuarkusTest
@TestTransaction
public class PersonRepositoryTests {

    @Inject
    PersonRepository personRepository;

    @Test
    void addPerson() {
        Person newPerson = new Person();
        newPerson.age = 22;
        newPerson.name = "TestNew";
        newPerson.gender = Gender.FEMALE;
        personRepository.persist(newPerson);
        Assertions.assertNotNull(newPerson.id);
    }
}

 

提示 10. 利用 GraphQL 支持

這是本文中的最後一個 Quarkus 技巧。然而,它是我最喜歡的 Quarkus 功能之一。GraphQL 支持並不是 Spring Boot 的強項。另一方面,Quarkus 為客戶端和服務器端的 GraphQL 提供了非常酷且簡單的擴展。

首先,讓我們添加負責 GraphQL 支持的 Quarkus 模塊。

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-graphql</artifactId>
</dependency>
<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-graphql-client</artifactId>
   <scope>test</scope>
</dependency>

然後我們可以創建一個代碼來負責暴露 GraphQL API。該類必須用 註釋@GraphQLAPI。Quarkus 自動從源代碼生成 GraphQL 模式。

@GraphQLApi
public class EmployeeFetcher {

    private EmployeeRepository repository;

    public EmployeeFetcher(EmployeeRepository repository){
        this.repository = repository;
    }

    @Query("employees")
    public List<Employee> findAll() {
        return repository.listAll();
    }

    @Query("employee")
    public Employee findById(@Name("id") Long id) {
        return repository.findById(id);
    }

    @Query("employeesWithFilter")
    public List<Employee> findWithFilter(@Name("filter") EmployeeFilter filter) {
        return repository.findByCriteria(filter);
    }

}

然後,讓我們創建一個客戶端接口來調用兩個端點。我們需要用 註釋該接口@GraphQLClientApi

@GraphQLClientApi(configKey = "employee-client")
public interface EmployeeClient {

    List<Employee> employees();
    Employee employee(Long id);
}

最後,我們可以添加一個簡單的 JUnit 測試。我們只需要注入EmployeeClient,然後調用方法。如果您對有關 Quarkus GraphQL 支持的更多詳細信息感興趣,請閱讀我的文章An Advanced GraphQL with Quarkus

@QuarkusTest
public class EmployeeFetcherTests {

    @Inject
    EmployeeClient employeeClient;

    @Test
    void fetchAll() {
        List<Employee> employees = employeeClient.employees();
        Assertions.assertEquals(10, employees.size());
    }

    @Test
    void fetchById() {
        Employee employee = employeeClient.employee(10L);
        Assertions.assertNotNull(employee);
    }
}

最後的想法

在我看來,Quarkus 是一個非常有趣和有前途的框架。借助這些技巧,您可以輕鬆地開始使用 Quarkus 開發您的第一個應用程序。每個新版本中都有一些有趣的新功能。所以也許,我將不得不盡快更新這個 Quarkus 提示列表

鏈接:https ://piotrminkowski.com/2021/10/12/quarkus-tips-tricks-and-techniques/

#quarkus #java #tips

與 Quarkus 框架相關的 10 大技巧和竅門
Dang  Tu

Dang Tu

1659777840

10 Mẹo Và Thủ Thuật Hàng đầu Liên Quan đến Khuôn Khổ Quarkus

Trong bài viết này, bạn sẽ tìm hiểu một số mẹo và thủ thuật hữu ích liên quan đến khuôn khổ Quarkus. Chúng tôi sẽ tập trung vào các tính năng nổi bật của Quarkus so với các khuôn khổ khác

Nếu bạn chạy các ứng dụng của mình trên Kubernetes, Quarkus rõ ràng là một lựa chọn tốt. Nó khởi động nhanh và không tốn nhiều bộ nhớ. Bạn có thể dễ dàng biên dịch nó nguyên bản với GraalVM. Nó cung cấp rất nhiều tính năng hữu ích cho nhà phát triển như tải lại nóng. Tôi hy vọng bạn sẽ tìm thấy các mẹo và kỹ thuật giúp tăng năng suất của bạn trong quá trình phát triển Quarkus. Hoặc có thể chỉ thuyết phục bạn xem qua nó, nếu bạn chưa có bất kỳ kinh nghiệm nào.

Mục lục

  • Mẹo 1. Sử dụng công cụ dòng lệnh Quarkus
  • Mẹo 2. Sử dụng Dịch vụ Dev với cơ sở dữ liệu
  • Mẹo 3. Sử dụng ORM đơn giản với Panache
  • Mẹo 4. Cấu hình hợp nhất làm tùy chọn
  • Mẹo 5. Triển khai Kubernetes với Maven
  • Mẹo 6. Truy cập bảng điều khiển giao diện người dùng Dev
  • Mẹo 7. Kiểm tra liên tục
  • Mẹo 8. Biên dịch nguyên bản với GraalVM trên OpenShift
  • Mẹo 9. Giao dịch khôi phục sau mỗi lần thử nghiệm
  • Mẹo 10. Tận dụng hỗ trợ GraphQL
  • Những Thougths cuối cùng

Mẹo 1. Sử dụng công cụ dòng lệnh Quarkus

Làm thế nào để bạn bắt đầu một ứng dụng mới khi sử dụng một trong những khuôn khổ Java phổ biến? Bạn có thể truy cập trang web của trình tạo trực tuyến, thường được cung cấp bởi các khuôn khổ đó. Bạn có nghe nói về Spring Initializr? Quarkus cung cấp một trang web tương tự tại https://code.quarkus.io/ . Nhưng có thể bạn chưa biết, còn có công cụ Quarkus CLI. Nó cho phép bạn tạo các dự án, quản lý các phần mở rộng và thực hiện các lệnh xây dựng và nhà phát triển. Ví dụ: bạn có thể tạo mã nguồn cho một ứng dụng mới bằng một lệnh duy nhất như hình dưới đây.

$ quarkus create app --package-name=pl.piomin.samples.quarkus \
  -x resteasy-jackson,hibernate-orm-panache,jdbc-postgresql \
  -o person-service \
  pl.piomin.samples:person-service

Sau khi thực hiện lệnh hiển thị ở trên, bạn sẽ thấy một màn hình tương tự.

quarkus-tips-cli

Lệnh này tạo một ứng dụng REST đơn giản sử dụng cơ sở dữ liệu PostgreSQL và lớp Quarkus ORM. Ngoài ra, nó đặt tên của ứng dụng, Maven groupIdartifactId. Sau đó, bạn chỉ có thể chạy ứng dụng. Để làm điều đó, hãy chuyển đến thư mục đã tạo và chạy lệnh sau. Ngoài ra, bạn có thể thực hiện mvn quarkus:devlệnh.

$ quarkus dev

Ứng dụng không khởi động thành công, vì không có kết nối cơ sở dữ liệu nào được định cấu hình. Chúng ta có phải làm như vậy không? Không! Chúng ta hãy chuyển sang phần tiếp theo để xem tại sao.

Mẹo 2. Sử dụng Dịch vụ Dev với cơ sở dữ liệu

Bạn có nghe nói về Testcontainers không? Nó là một thư viện Java cho phép bạn chạy các vùng chứa tự động trong các bài kiểm tra JUnit. Bạn có thể chạy cơ sở dữ liệu chung, trình duyệt web Selenium hoặc bất kỳ thứ gì khác có thể chạy trong vùng chứa Docker. Quarkus cung cấp khả năng tích hợp sẵn với Testcontainers khi chạy các ứng dụng ở chế độ dev hoặc test. Tính năng này được gọi là Dịch vụ nhà phát triển. Hơn nữa, bạn không phải làm bất cứ điều gì để kích hoạt nó. Chỉ KHÔNG CUNG CẤP URL kết nối và thông tin đăng nhập.

Hãy quay lại kịch bản của chúng ta. Chúng tôi đã tạo ứng dụng bằng Quarkus CLI. Nó chứa tất cả các thư viện cần thiết. Vì vậy, điều duy nhất chúng ta cần làm bây giờ là chạy một trình nền Docker. Nhờ đó Quarkus sẽ cố gắng chạy PostgreSQL với Testcontainers ở chế độ phát triển. Kết quả cuối cùng là gì? Ứng dụng của chúng tôi đang hoạt động và nó được kết nối với PostgreSQL bắt đầu bằng Docker như hình dưới đây.

Sau đó, chúng tôi có thể tiến hành phát triển. Với quarkus devlệnh, chúng tôi đã kích hoạt chế độ nhà phát triển. Nhờ đó, chúng tôi có thể tận dụng tính năng tải lại trực tiếp.

Mẹo 3. Sử dụng ORM đơn giản với Panache

Hãy thêm một số mã vào ứng dụng mẫu của chúng tôi. Chúng tôi sẽ triển khai một lớp dữ liệu bằng cách sử dụng Quarkus Panache ORM. Đó là một mô-đun rất thú vị tập trung vào việc làm cho các thực thể của bạn trở nên tầm thường và thú vị khi viết trong Quarkus. Đây là lớp thực thể của chúng tôi. Nhờ ghi lại quyền truy cập trường Quarkus, khi bạn đọc,  person.name bạn sẽ thực sự gọi  trình truy cập của mình getName() , và tương tự đối với ghi trường và trình thiết lập. Điều này cho phép đóng gói thích hợp trong thời gian chạy vì tất cả các lệnh gọi trường sẽ được thay thế bằng các lệnh gọi getter hoặc setter tương ứng. Nó PanacheEntitycũng chăm sóc việc triển khai khóa chính.

@Entity
public class Person extends PanacheEntity {
   public String name;
   public int age;
   @Enumerated(EnumType.STRING)
   public Gender gender;
}

Trong bước tiếp theo, chúng ta sẽ xác định lớp kho lưu trữ. Vì nó triển khai PanacheRepositorygiao diện, chúng tôi chỉ cần thêm các phương pháp tìm tùy chỉnh của mình.

@ApplicationScoped
public class PersonRepository implements PanacheRepository<Person> {

    public List<Person> findByName(String name) {
        return find("name", name).list();
    }

    public List<Person> findByAgeGreaterThan(int age) {
        return find("age > ?1", age).list();
    }
}

Cuối cùng, hãy thêm một lớp tài nguyên với các điểm cuối REST.

@Path("/persons")
public class PersonResource {

    @Inject
    PersonRepository personRepository;

    @POST
    @Transactional
    public Person addPerson(Person person) {
        personRepository.persist(person);
        return person;
    }

    @GET
    public List<Person> getPersons() {
        return personRepository.listAll();
    }

    @GET
    @Path("/name/{name}")
    public List<Person> getPersonsByName(@PathParam("name") String name) {
        return personRepository.findByName(name);
    }

    @GET
    @Path("/age-greater-than/{age}")
    public List<Person> getPersonsByName(@PathParam("age") int age) {
        return personRepository.findByAgeGreaterThan(age);
    }

    @GET
    @Path("/{id}")
    public Person getPersonById(@PathParam("id") Long id) {
        return personRepository.findById(id);
    }

}

Ngoài ra, hãy tạo import.sqltệp trong thư mục src/main/resources. Nó tải các câu lệnh SQL khi ORM Hibernate bắt đầu.

insert into person(id, name, age, gender) values(1, 'John Smith', 25, 'MALE');
insert into person(id, name, age, gender) values(2, 'Paul Walker', 65, 'MALE');
insert into person(id, name, age, gender) values(3, 'Lewis Hamilton', 35, 'MALE');
insert into person(id, name, age, gender) values(4, 'Veronica Jones', 20, 'FEMALE');
insert into person(id, name, age, gender) values(5, 'Anne Brown', 60, 'FEMALE');
insert into person(id, name, age, gender) values(6, 'Felicia Scott', 45, 'FEMALE');

Cuối cùng, chúng ta có thể gọi điểm cuối REST của mình.

$ curl http://localhost:8080/persons

 

Mẹo 4. Cấu hình hợp nhất làm tùy chọn

Giả sử chúng ta không muốn chạy cơ sở dữ liệu trên Docker, chúng ta nên định cấu hình kết nối trong application.properties. Theo mặc định, Quarkus cung cấp 3 cấu hình : prod,, . Chúng ta có thể xác định các thuộc tính cho nhiều cấu hình bên trong một cấu hình duy nhất bằng cách sử dụng cú pháp  . Trong trường hợp của chúng tôi, có một cá thể H2 được sử dụng trong và các chế độ, và một cá thể PostgreSQL bên ngoài trong chế độ.testdevapplication.properties%{profile-name}.config.namedevtestprod

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${POSTGRES_USER}
quarkus.datasource.password=${POSTGRES_PASSWORD}
quarkus.datasource.jdbc.url=jdbc:postgresql://person-db:5432/${POSTGRES_DB}

%test.quarkus.datasource.db-kind=h2
%test.quarkus.datasource.username=sa
%test.quarkus.datasource.password=password
%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb

%dev.quarkus.datasource.db-kind=h2
%dev.quarkus.datasource.username=sa
%dev.quarkus.datasource.password=password
%dev.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb

Trước khi chạy một ứng dụng phiên bản mới, chúng ta phải đưa sự phụ thuộc H2 vào Maven pom.xml.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-jdbc-h2</artifactId>
</dependency>

Bạn cũng có thể xác định cấu hình tùy chỉnh của mình và cung cấp các thuộc tính bằng cách sử dụng nó làm tiền tố. Tất nhiên, bạn vẫn có thể xác định các tệp hồ sơ cụ thể như application-{profile}.properties.

Mẹo 5. Triển khai Kubernetes với Maven

Quarkus trong khung bản địa Kubernetes. Bạn có thể dễ dàng triển khai ứng dụng Quarkus của mình vào cụm Kubernetes mà không cần tạo bất kỳ tệp YAML nào theo cách thủ công. Đối với các cấu hình nâng cao hơn, chẳng hạn như ánh xạ bí mật với các biến môi trường, bạn có thể sử dụng application.properties. Những thứ khác như kiểm tra sức khỏe được phát hiện trong mã nguồn. Để kích hoạt điều này, chúng ta cần bao gồm quarkus-kubernetesmô-đun. Ngoài ra còn có một triển khai cho OpenShift.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-openshift</artifactId>
</dependency>

Sau đó, Quarkus sẽ tạo các bản kê khai triển khai trong quá trình xây dựng Maven. Chúng tôi có thể kích hoạt triển khai tự động cho cụm Kubernetes hiện tại bằng cách đặt thuộc tính quarkus.kubernetes.deploythành true. Để triển khai OpenShift, chúng ta phải thay đổi mục tiêu triển khai mặc định từ kubernetesthành openshift.

quarkus.container-image.build = true
quarkus.kubernetes.deploy = true
quarkus.kubernetes.deployment-target = openshift

Giả sử chúng ta có một số cấu hình tùy chỉnh để đặt trên Deploymenttệp kê khai. Ứng dụng của chúng tôi sẽ chạy trong hai nhóm và tự động được hiển thị bên ngoài cụm. Nó cũng đưa các giá trị từ Secretđể kết nối với cơ sở dữ liệu PostgreSQL.

quarkus.openshift.expose = true
quarkus.openshift.replicas = 2
quarkus.openshift.labels.app = person-app
quarkus.openshift.annotations.app-type = demo
quarkus.openshift.env.mapping.postgres_user.from-secret = person-db
quarkus.openshift.env.mapping.postgres_user.with-key = database-user
quarkus.openshift.env.mapping.postgres_password.from-secret = person-db
quarkus.openshift.env.mapping.postgres_password.with-key = database-password
quarkus.openshift.env.mapping.postgres_db.from-secret = person-db
quarkus.openshift.env.mapping.postgres_db.with-key = database-name

Sau đó, chúng tôi chỉ cần xây dựng ứng dụng của mình với Maven. Ngoài ra, chúng tôi có thể xóa thuộc quarkus.kubernetes.deploytính khỏi application.propertiesvà bật thuộc tính đó trên lệnh Maven.

$ maven clean package -D<meta charset="utf-8">quarkus.kubernetes.deploy=true

 

Mẹo 6. Truy cập bảng điều khiển giao diện người dùng Dev

Sau khi chạy ứng dụng Quarkus ở chế độ dev ( mvn<em> </em>quarkus:dev), bạn có thể truy cập bảng điều khiển Dev UI theo địa chỉ http: // localhost: 8080 / q / dev . Bạn càng bao gồm nhiều mô-đun, bạn có thể cấu hình nhiều tùy chọn hơn ở đó. Một trong những tính năng yêu thích của tôi ở đây là khả năng triển khai ứng dụng cho OpenShift. Thay vì chạy lệnh Maven để xây dựng ứng dụng, chúng ta có thể chạy nó ở devchế độ và triển khai bằng giao diện người dùng đồ họa.

quarkus-tips-dev-ui

 

Mẹo 7. Kiểm tra liên tục

Quarkus hỗ trợ kiểm tra liên tục, nơi các bài kiểm tra chạy ngay lập tức sau khi thay đổi mã. Điều này cho phép bạn nhận được phản hồi tức thì về các thay đổi mã của bạn. Quarkus phát hiện kiểm tra nào bao gồm mã nào và sử dụng thông tin này để chỉ chạy các kiểm tra liên quan khi mã được thay đổi. Sau khi chạy ứng dụng đang được phát triển, bạn sẽ được nhắc kích hoạt tính năng đó như hình dưới đây. Chỉ cần nhấn rđể kích hoạt nó.

Được rồi, chúng ta hãy thêm một số thử nghiệm cho ứng dụng mẫu của chúng ta. Đầu tiên, chúng ta cần bao gồm mô-đun Kiểm tra Quarkus và thư viện REST Assured.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-junit5</artifactId>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>io.rest-assured</groupId>
   <artifactId>rest-assured</artifactId>
   <scope>test</scope>
</dependency>

Sau đó, chúng tôi sẽ thêm một số thử nghiệm API đơn giản. Lớp thử nghiệm phải được chú thích với @QuarkusTest. Phần còn lại của việc triển khai là điển hình cho thư viện REST Assured.

@QuarkusTest
public class PersonResourceTests {

    @Test
    void getPersons() {
        List<Person> persons = given().when().get("/persons")
                .then()
                .statusCode(200)
                .extract()
                .body()
                .jsonPath().getList(".", Person.class);
        assertEquals(persons.size(), 6);
    }

    @Test
    void getPersonById() {
        Person person = given()
                .pathParam("id", 1)
                .when().get("/persons/{id}")
                .then()
                .statusCode(200)
                .extract()
                .body().as(Person.class);
        assertNotNull(person);
        assertEquals(1L, person.id);
    }

    @Test
    void newPersonAdd() {
        Person newPerson = new Person();
        newPerson.age = 22;
        newPerson.name = "TestNew";
        newPerson.gender = Gender.FEMALE;
        Person person = given()
                .body(newPerson)
                .contentType(ContentType.JSON)
                .when().post("/persons")
                .then()
                .statusCode(200)
                .extract()
                .body().as(Person.class);
        assertNotNull(person);
        assertNotNull(person.id);
    }
}

Chúng tôi cũng có thể chạy các bài kiểm tra JUnit đó từ bảng điều khiển Dev UI. Trước tiên, bạn nên chuyển đến giao diện điều khiển Dev UI. Ở cuối trang, bạn sẽ tìm thấy mô-đun kiểm tra chịu trách nhiệm của bảng điều khiển. Chỉ cần nhấp vào biểu tượng Kết quả kiểm tra và bạn sẽ thấy một màn hình tương tự như bên dưới.

 

Mẹo 8. Biên dịch nguyên bản với GraalVM trên OpenShift

Bạn có thể dễ dàng tạo và chạy hình ảnh Quarkus GraalVM gốc trên OpenShift bằng cách sử dụng một lệnh và trình tạo duy nhất ubi-quarkus-native-s2i. OpenShift xây dựng ứng dụng bằng cách tiếp cận S2I (nguồn-2-hình ảnh). Tất nhiên, bạn chỉ cần một cụm OpenShift đang chạy (ví dụ CRC cục bộ hoặc Hộp cát nhà phát triển https://developers.redhat.com/products/codeready-containers/overview ) và ocứng dụng khách được cài đặt cục bộ.

$ oc new-app --name person-native \
             --context-dir basic-with-db/person-app \
  quay.io/quarkus/ubi-quarkus-native-s2i:21.2-java11~https://github.com/piomin/openshift-quickstart.git

Mẹo 9. Giao dịch khôi phục sau mỗi lần thử nghiệm

Nếu bạn cần khôi phục các thay đổi trong dữ liệu sau mỗi lần kiểm tra, hãy tránh thực hiện theo cách thủ công. Thay vào đó, bạn chỉ cần chú thích lớp thử nghiệm của mình với @TestTransaction. Khôi phục được thực hiện mỗi khi phương pháp thử nghiệm hoàn tất.

@QuarkusTest
@TestTransaction
public class PersonRepositoryTests {

    @Inject
    PersonRepository personRepository;

    @Test
    void addPerson() {
        Person newPerson = new Person();
        newPerson.age = 22;
        newPerson.name = "TestNew";
        newPerson.gender = Gender.FEMALE;
        personRepository.persist(newPerson);
        Assertions.assertNotNull(newPerson.id);
    }
}

 

Mẹo 10. Tận dụng hỗ trợ GraphQL

Đó là mẹo cuối cùng của Quarkus trong bài viết này. Tuy nhiên, nó là một trong những tính năng Quarkus yêu thích của tôi. Hỗ trợ GraphQL không phải là một mặt mạnh của Spring Boot. Mặt khác, Quarkus cung cấp các phần mở rộng rất thú vị và đơn giản cho GraphQL cho phía máy khách và phía máy chủ.

Đầu tiên, hãy thêm các mô-đun Quarkus chịu trách nhiệm hỗ trợ GraphQL.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-graphql</artifactId>
</dependency>
<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-graphql-client</artifactId>
   <scope>test</scope>
</dependency>

Sau đó, chúng tôi có thể tạo một mã chịu trách nhiệm hiển thị API GraphQL. Lớp phải được chú thích với @GraphQLAPI. Quarkus tự động tạo lược đồ GraphQL từ mã nguồn.

@GraphQLApi
public class EmployeeFetcher {

    private EmployeeRepository repository;

    public EmployeeFetcher(EmployeeRepository repository){
        this.repository = repository;
    }

    @Query("employees")
    public List<Employee> findAll() {
        return repository.listAll();
    }

    @Query("employee")
    public Employee findById(@Name("id") Long id) {
        return repository.findById(id);
    }

    @Query("employeesWithFilter")
    public List<Employee> findWithFilter(@Name("filter") EmployeeFilter filter) {
        return repository.findByCriteria(filter);
    }

}

Sau đó, hãy tạo một giao diện khách để gọi hai điểm cuối. Chúng tôi cần chú thích giao diện đó với @GraphQLClientApi.

@GraphQLClientApi(configKey = "employee-client")
public interface EmployeeClient {

    List<Employee> employees();
    Employee employee(Long id);
}

Cuối cùng, chúng ta có thể thêm một bài kiểm tra JUnit đơn giản. Chúng ta chỉ cần tiêm EmployeeClient, và sau đó gọi các phương thức. Nếu bạn quan tâm đến chi tiết hơn về hỗ trợ Quarkus GraphQL, hãy đọc bài viết của tôi An Advanced GraphQL với Quarkus .

@QuarkusTest
public class EmployeeFetcherTests {

    @Inject
    EmployeeClient employeeClient;

    @Test
    void fetchAll() {
        List<Employee> employees = employeeClient.employees();
        Assertions.assertEquals(10, employees.size());
    }

    @Test
    void fetchById() {
        Employee employee = employeeClient.employee(10L);
        Assertions.assertNotNull(employee);
    }
}

Những Thougths cuối cùng

Theo tôi, Quarkus là một framework rất thú vị và đầy hứa hẹn. Với những thủ thuật này, bạn có thể dễ dàng bắt đầu phát triển ứng dụng đầu tiên của mình với Quarkus. Có một số tính năng thú vị mới trong mỗi bản phát hành mới. Vì vậy, có lẽ, tôi sẽ phải cập nhật danh sách các thủ thuật Quarkus này sớm

Liên kết: https://piotrminkowski.com/2021/10/12/quarkus-tips-tricks-and-techniques/

#quarkus #java #tips

10 Mẹo Và Thủ Thuật Hàng đầu Liên Quan đến Khuôn Khổ Quarkus

10 лучших советов и приемов, связанных с Quarkus Framework

В этой статье вы узнаете несколько полезных советов и приемов, связанных с фреймворком Quarkus. Мы сосредоточимся на функциях, которые отличают Quarkus от других фреймворков.

Если вы запускаете свои приложения в Kubernetes, Quarkus, очевидно, является хорошим выбором. Запускается быстро и не потребляет много памяти. Вы можете легко скомпилировать его с помощью GraalVM. Он предоставляет множество полезных функций для разработчиков, таких как, например, горячая перезагрузка. Надеюсь, вы найдете там советы и приемы, которые помогут повысить вашу продуктивность при разработке Quarkus. Или, может быть, просто убедить вас взглянуть на это, если у вас еще нет опыта.

Оглавление

  • Совет 1. Используйте инструмент командной строки Quarkus
  • Совет 2. Используйте Dev Services с базами данных
  • Совет 3. Используйте упрощенный ORM с Panache
  • Совет 4. Унифицированная конфигурация как вариант
  • Совет 5. Разверните в Kubernetes с помощью Maven
  • Совет 6. Получите доступ к консоли Dev UI
  • Совет 7. Постоянно тестируйте
  • Совет 8. Скомпилируйте с помощью GraalVM на OpenShift
  • Совет 9. Откатывайте транзакцию после каждого теста
  • Совет 10. Воспользуйтесь преимуществами поддержки GraphQL
  • Заключительные мысли

Совет 1. Используйте инструмент командной строки Quarkus

Как запустить новое приложение при использовании одной из популярных сред Java? Вы можете перейти на веб-сайт онлайн-генератора, который обычно предоставляется этими фреймворками. Вы слышали о Spring Initializr? Quarkus предлагает аналогичный сайт по адресу https://code.quarkus.io/ . Но что вы, возможно, не знаете, так это инструмент командной строки Quarkus. Он позволяет создавать проекты, управлять расширениями и выполнять команды сборки и разработки. Например, вы можете создать исходный код для нового приложения с помощью одной команды, как показано ниже.

$ quarkus create app --package-name=pl.piomin.samples.quarkus \
  -x resteasy-jackson,hibernate-orm-panache,jdbc-postgresql \
  -o person-service \
  pl.piomin.samples:person-service

После выполнения команды, показанной выше, вы должны увидеть аналогичный экран.

quarkus-советы-кли

Эта команда создает простое приложение REST, использующее базу данных PostgreSQL и уровень ORM Quarkus. Кроме того, он устанавливает имя приложения, Maven groupIdи artifactId. После этого вы можете просто запустить приложение. Для этого перейдите в сгенерированный каталог и выполните следующую команду. Кроме того, вы можете выполнить mvn quarkus:devкоманду.

$ quarkus dev

Приложение не запускается успешно, так как не настроено подключение к базе данных. Должны ли мы это делать? Нет! Давайте перейдем к следующему разделу, чтобы понять, почему.

Совет 2. Используйте Dev Services с базами данных

Вы слышали о тестовых контейнерах? Это библиотека Java, которая позволяет автоматически запускать контейнеры во время тестов JUnit. Вы можете запускать обычные базы данных, веб-браузеры Selenium или что-то еще, что может работать в контейнере Docker. Quarkus обеспечивает встроенную интеграцию с тестовыми контейнерами при запуске приложений в режимах разработки или тестирования. Эта функция называется Dev Services. Более того, вам не нужно ничего делать, чтобы включить его. Просто НЕ ПРЕДОСТАВЛЯЙТЕ URL-адрес подключения и учетные данные.

Вернемся к нашему сценарию. Мы уже создали приложение с помощью Quarkus CLI. Он содержит все необходимые библиотеки. Итак, единственное, что нам нужно сделать сейчас, это запустить демон Docker. Благодаря этому Quarkus попытается запустить PostgreSQL с тестовыми контейнерами в режиме разработки. Каков конечный результат? Наше приложение работает и связано с PostgreSQL, запущенным с помощью Docker, как показано ниже.

Тогда можно переходить к разработке. С помощью quarkus devкоманды мы уже включили режим разработки. Благодаря этому мы можем воспользоваться функцией перезагрузки в реальном времени.

Совет 3. Используйте упрощенный ORM с Panache

Давайте добавим немного кода в наше примерное приложение. Мы реализуем уровень данных с помощью Quarkus Panache ORM. Это очень интересный модуль, который фокусируется на том, чтобы сделать ваши сущности тривиальными и увлекательными для написания в Quarkus. Вот наш класс сущности. Благодаря перезаписи доступа к полю Quarkus, когда вы читаете  person.name , вы фактически вызываете свой  метод доступа getName() , и аналогично для записи в поле и сеттера. Это обеспечивает правильную инкапсуляцию во время выполнения, поскольку все вызовы полей будут заменены соответствующими вызовами геттера или сеттера. Также PanacheEntityпозаботится о реализации первичного ключа.

@Entity
public class Person extends PanacheEntity {
   public String name;
   public int age;
   @Enumerated(EnumType.STRING)
   public Gender gender;
}

На следующем шаге мы собираемся определить класс репозитория. Поскольку он реализует PanacheRepositoryинтерфейс, нам нужно только добавить наши собственные методы поиска.

@ApplicationScoped
public class PersonRepository implements PanacheRepository<Person> {

    public List<Person> findByName(String name) {
        return find("name", name).list();
    }

    public List<Person> findByAgeGreaterThan(int age) {
        return find("age > ?1", age).list();
    }
}

Наконец, давайте добавим класс ресурсов с конечными точками REST.

@Path("/persons")
public class PersonResource {

    @Inject
    PersonRepository personRepository;

    @POST
    @Transactional
    public Person addPerson(Person person) {
        personRepository.persist(person);
        return person;
    }

    @GET
    public List<Person> getPersons() {
        return personRepository.listAll();
    }

    @GET
    @Path("/name/{name}")
    public List<Person> getPersonsByName(@PathParam("name") String name) {
        return personRepository.findByName(name);
    }

    @GET
    @Path("/age-greater-than/{age}")
    public List<Person> getPersonsByName(@PathParam("age") int age) {
        return personRepository.findByAgeGreaterThan(age);
    }

    @GET
    @Path("/{id}")
    public Person getPersonById(@PathParam("id") Long id) {
        return personRepository.findById(id);
    }

}

Кроме того, давайте создадим import.sqlфайл в src/main/resourcesкаталоге. Он загружает операторы SQL при запуске Hibernate ORM.

insert into person(id, name, age, gender) values(1, 'John Smith', 25, 'MALE');
insert into person(id, name, age, gender) values(2, 'Paul Walker', 65, 'MALE');
insert into person(id, name, age, gender) values(3, 'Lewis Hamilton', 35, 'MALE');
insert into person(id, name, age, gender) values(4, 'Veronica Jones', 20, 'FEMALE');
insert into person(id, name, age, gender) values(5, 'Anne Brown', 60, 'FEMALE');
insert into person(id, name, age, gender) values(6, 'Felicia Scott', 45, 'FEMALE');

Наконец, мы можем вызвать нашу конечную точку REST.

$ curl http://localhost:8080/persons

 

Совет 4. Унифицированная конфигурация как вариант

Предполагая, что мы не хотим запускать базу данных в Docker, мы должны настроить соединение в application.properties. По умолчанию Quarkus предоставляет 3 профиля: prod, test, dev. Мы можем определить свойства для нескольких профилей внутри одного, application.propertiesиспользуя синтаксис  %{profile-name}.config.name. В нашем случае есть экземпляр H2, используемый в режимах devи test, и внешний экземпляр PostgreSQL в prodрежиме.

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${POSTGRES_USER}
quarkus.datasource.password=${POSTGRES_PASSWORD}
quarkus.datasource.jdbc.url=jdbc:postgresql://person-db:5432/${POSTGRES_DB}

%test.quarkus.datasource.db-kind=h2
%test.quarkus.datasource.username=sa
%test.quarkus.datasource.password=password
%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb

%dev.quarkus.datasource.db-kind=h2
%dev.quarkus.datasource.username=sa
%dev.quarkus.datasource.password=password
%dev.quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb

Перед запуском новой версии приложения мы должны включить зависимость H2 в Maven pom.xml.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-jdbc-h2</artifactId>
</dependency>

Вы также можете определить свой собственный профиль и указать свойства, используя его в качестве префикса. Конечно, вы по-прежнему можете определять файлы для конкретных профилей, такие как application-{profile}.properties.

Совет 5. Разверните в Kubernetes с помощью Maven

Quarkus в нативной среде Kubernetes. Вы можете легко развернуть приложение Quarkus в кластере Kubernetes без создания файлов YAML вручную. Для более сложных конфигураций, таких как, например, сопоставление секретов с переменными среды, вы можете использовать application.properties. Другие вещи, такие как, например, проверки работоспособности, обнаруживаются в исходном коде. Для этого нам нужно подключить quarkus-kubernetesмодуль. Также есть реализация для OpenShift.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-openshift</artifactId>
</dependency>

После этого Quarkus создаст манифесты развертывания во время сборки Maven. Мы можем включить автоматическое развертывание в текущем кластере Kubernetes, установив для свойства quarkus.kubernetes.deployзначение true. Для развертывания OpenShift мы должны изменить цель развертывания по умолчанию с kubernetesна openshift.

quarkus.container-image.build = true
quarkus.kubernetes.deploy = true
quarkus.kubernetes.deployment-target = openshift

Предположим, у нас есть некоторая пользовательская конфигурация для установки в Deploymentманифесте. Наше приложение будет работать в двух модулях и автоматически будет отображаться за пределами кластера. Он также вводит значения Secretдля подключения к базе данных PostgreSQL.

quarkus.openshift.expose = true
quarkus.openshift.replicas = 2
quarkus.openshift.labels.app = person-app
quarkus.openshift.annotations.app-type = demo
quarkus.openshift.env.mapping.postgres_user.from-secret = person-db
quarkus.openshift.env.mapping.postgres_user.with-key = database-user
quarkus.openshift.env.mapping.postgres_password.from-secret = person-db
quarkus.openshift.env.mapping.postgres_password.with-key = database-password
quarkus.openshift.env.mapping.postgres_db.from-secret = person-db
quarkus.openshift.env.mapping.postgres_db.with-key = database-name

Затем нам просто нужно собрать наше приложение с помощью Maven. В качестве альтернативы мы можем удалить это quarkus.kubernetes.deployсвойство application.propertiesи включить его в команде Maven.

$ maven clean package -D<meta charset="utf-8">quarkus.kubernetes.deploy=true

 

Совет 6. Получите доступ к консоли Dev UI

После запуска приложения Quarkus в режиме разработки ( mvn<em> </em>quarkus:dev) вы можете получить доступ к консоли Dev UI по адресу http://localhost:8080/q/dev . Чем больше модулей вы включаете, тем больше параметров вы можете там настроить. Одна из моих любимых функций здесь — возможность развертывания приложений в OpenShift. Вместо запуска команды Maven для создания приложения мы можем просто запустить его в devрежиме и развернуть с помощью графического интерфейса.

quarkus-советы-dev-ui

 

Совет 7. Постоянно тестируйте

Quarkus поддерживает непрерывное тестирование, когда тесты запускаются сразу после изменения кода. Это позволяет вам мгновенно получать отзывы об изменениях кода. Quarkus определяет, какие тесты покрывают какой код, и использует эту информацию для запуска соответствующих тестов только при изменении кода. После запуска приложения в разработке вам будет предложено включить эту функцию, как показано ниже. Просто нажмите r, чтобы включить его.

Итак, давайте добавим несколько тестов для нашего примера приложения. Во-первых, нам нужно включить тестовый модуль Quarkus и библиотеку REST Assured.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-junit5</artifactId>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>io.rest-assured</groupId>
   <artifactId>rest-assured</artifactId>
   <scope>test</scope>
</dependency>

Затем мы добавим несколько простых тестов API. Тестовый класс должен быть аннотирован с помощью @QuarkusTest. В остальном реализация типична для библиотеки REST Assured.

@QuarkusTest
public class PersonResourceTests {

    @Test
    void getPersons() {
        List<Person> persons = given().when().get("/persons")
                .then()
                .statusCode(200)
                .extract()
                .body()
                .jsonPath().getList(".", Person.class);
        assertEquals(persons.size(), 6);
    }

    @Test
    void getPersonById() {
        Person person = given()
                .pathParam("id", 1)
                .when().get("/persons/{id}")
                .then()
                .statusCode(200)
                .extract()
                .body().as(Person.class);
        assertNotNull(person);
        assertEquals(1L, person.id);
    }

    @Test
    void newPersonAdd() {
        Person newPerson = new Person();
        newPerson.age = 22;
        newPerson.name = "TestNew";
        newPerson.gender = Gender.FEMALE;
        Person person = given()
                .body(newPerson)
                .contentType(ContentType.JSON)
                .when().post("/persons")
                .then()
                .statusCode(200)
                .extract()
                .body().as(Person.class);
        assertNotNull(person);
        assertNotNull(person.id);
    }
}

Мы также можем запускать эти тесты JUnit из консоли Dev UI. Во-первых, вы должны перейти в консоль Dev UI. Внизу страницы вы найдете модуль ответственного тестирования панели. Просто щелкните значок Результат теста , и вы увидите экран, подобный показанному ниже.

 

Совет 8. Скомпилируйте с помощью GraalVM на OpenShift

Вы можете легко создать и запустить собственный образ Quarkus GraalVM в OpenShift с помощью одной команды и сборщика ubi-quarkus-native-s2i. OpenShift создает приложение, используя подход S2I (исходный код 2 образа). Конечно, вам просто нужен работающий кластер OpenShift (например, локальный CRC или тестовая среда разработчика https://developers.redhat.com/products/codeready-containers/overview ) и ocклиент, установленный локально.

$ oc new-app --name person-native \
             --context-dir basic-with-db/person-app \
  quay.io/quarkus/ubi-quarkus-native-s2i:21.2-java11~https://github.com/piomin/openshift-quickstart.git

Совет 9. Откатывайте транзакцию после каждого теста

Если вам нужно откатить изменения в данных после каждого теста, не делайте этого вручную. Вместо этого вам просто нужно аннотировать свой тестовый класс с помощью @TestTransaction. Откат выполняется каждый раз, когда метод тестирования завершается.

@QuarkusTest
@TestTransaction
public class PersonRepositoryTests {

    @Inject
    PersonRepository personRepository;

    @Test
    void addPerson() {
        Person newPerson = new Person();
        newPerson.age = 22;
        newPerson.name = "TestNew";
        newPerson.gender = Gender.FEMALE;
        personRepository.persist(newPerson);
        Assertions.assertNotNull(newPerson.id);
    }
}

 

Совет 10. Воспользуйтесь преимуществами поддержки GraphQL

Это последний совет Quarkus в этой статье. Тем не менее, это одна из моих любимых функций Quarkus. Поддержка GraphQL не является сильной стороной Spring Boot. С другой стороны, Quarkus предоставляет очень классные и простые расширения для GraphQL для клиента и сервера.

Во-первых, добавим модули Quarkus, отвечающие за поддержку GraphQL.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-graphql</artifactId>
</dependency>
<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-graphql-client</artifactId>
   <scope>test</scope>
</dependency>

Затем мы можем создать код, отвечающий за предоставление GraphQL API. Класс должен быть аннотирован с помощью @GraphQLAPI. Quarkus автоматически генерирует схему GraphQL из исходного кода.

@GraphQLApi
public class EmployeeFetcher {

    private EmployeeRepository repository;

    public EmployeeFetcher(EmployeeRepository repository){
        this.repository = repository;
    }

    @Query("employees")
    public List<Employee> findAll() {
        return repository.listAll();
    }

    @Query("employee")
    public Employee findById(@Name("id") Long id) {
        return repository.findById(id);
    }

    @Query("employeesWithFilter")
    public List<Employee> findWithFilter(@Name("filter") EmployeeFilter filter) {
        return repository.findByCriteria(filter);
    }

}

Затем давайте создадим клиентский интерфейс для вызова двух конечных точек. Нам нужно аннотировать этот интерфейс с помощью @GraphQLClientApi.

@GraphQLClientApi(configKey = "employee-client")
public interface EmployeeClient {

    List<Employee> employees();
    Employee employee(Long id);
}

Наконец, мы можем добавить простой тест JUnit. Нам просто нужно внедрить EmployeeClient, а затем вызвать методы. Если вас интересуют более подробные сведения о поддержке Quarkus GraphQL, прочитайте мою статью An Advanced GraphQL with Quarkus .

@QuarkusTest
public class EmployeeFetcherTests {

    @Inject
    EmployeeClient employeeClient;

    @Test
    void fetchAll() {
        List<Employee> employees = employeeClient.employees();
        Assertions.assertEquals(10, employees.size());
    }

    @Test
    void fetchById() {
        Employee employee = employeeClient.employee(10L);
        Assertions.assertNotNull(employee);
    }
}

Заключительные мысли

На мой взгляд, Quarkus — очень интересный и перспективный фреймворк. С помощью этих советов вы легко сможете начать разработку своего первого приложения с помощью Quarkus. В каждом новом релизе появляются новые интересные функции. Так что, возможно, мне скоро придется обновить этот список советов по Quarkus.

Ссылка: https://piotrminkowski.com/2021/10/12/quarkus-tips-tricks-and-techniques/

#quarkus #java #tips

10 лучших советов и приемов, связанных с Quarkus Framework
Lawrence  Lesch

Lawrence Lesch

1659705960

javascript-tips-and-tidbits: A continuously-evolving compendium

A continuously-evolving compendium of javascript tips based on common areas of confusion or misunderstanding.


Value vs. Reference Variable Assignment

Understanding how JavaScript assigns to variables is foundational to writing bug-free JavaScript. If you don't understand this, you could easily write code that unintentionally changes values.

When JavaScript assigns one of the seven primitive type (i.e., Boolean, Null, Undefined, String, Number, Symbol, and BigInt.) to a variable, the JavaScript runtime gets to determine whether that primitive is assigned by reference or by value. It doesn't really matter how it's done because primitives can't be mutated (they're immutable). However, when the assigned value is an Array, Function, or Object a reference to the array/function/object in memory is assigned.

Example time! In the following snippet, var2 is set as equal to var1. Since var1 is a primitive type (String), var2 is set as equal to var1's String value and can be thought of as completely distinct from var1 at this point. Accordingly, reassigning var2 has no effect on var1.

const var1 = 'My string';
let var2 = var1;

var2 = 'My new string';

console.log(var1);
// 'My string'
console.log(var2);
// 'My new string'

Let's compare this with object assignment.

const var1 = { name: 'Jim' };
const var2 = var1;

var2.name = 'John';

console.log(var1);
// { name: 'John' }
console.log(var2);
// { name: 'John' }

How this is working:

  • The object { name: 'Jim' } is created in memory
  • The variable var1 is assigned a reference to the created object
  • The variable var2 is set to equal var1... which is a reference to that same object in memory!
  • var2 is mutated, which really means the object var2 is referencing is mutated
  • var1 is pointing to the same object as var2, and therefore we see this mutation when accessing var1

One might see how this could cause problems if you expected behavior like primitive assignment! This can get especially ugly if you create a function that unintentionally mutates an object.

For more on variable assignment and primitive/object mutability, see this article.

Closures

Closure is an important javascript pattern to give private access to a variable. In this example, createGreeter returns an anonymous function that has access to the supplied greeting, "Hello." For all future uses, sayHello will have access to this greeting!

function createGreeter(greeting) {
    return function(name) {
        console.log(greeting + ', ' + name);
    };
}

const sayHello = createGreeter('Hello');

sayHello('Joe');
// Hello, Joe

In a more real-world scenario, you could envision an initial function apiConnect(apiKey) that returns some methods that would use the API key. In this case, the apiKey would just need to be provided once and never again.

function apiConnect(apiKey) {
    function get(route) {
        return fetch(`${route}?key=${apiKey}`);
    }

    function post(route, params) {
        return fetch(route, {
            method: 'POST',
            body: JSON.stringify(params),
            headers: {
                Authorization: `Bearer ${apiKey}`
            }
        });
    }

    return { get, post };
}

const api = apiConnect('my-secret-key');

// No need to include the apiKey anymore
api.get('http://www.example.com/get-endpoint');
api.post('http://www.example.com/post-endpoint', { name: 'Joe' });

Destructuring

Don't be thrown off by javascript parameter destructuring! It's a common way to cleanly extract properties from objects.

const obj = {
    name: 'Joe',
    food: 'cake'
};

const { name, food } = obj;

console.log(name, food);
// 'Joe' 'cake'

If you want to extract properties under a different name, you can specify them using the following format.

const obj = {
    name: 'Joe',
    food: 'cake'
};

const { name: myName, food: myFood } = obj;

console.log(myName, myFood);
// 'Joe' 'cake'

In the following example, destructuring is used to cleanly pass the person object to the introduce function. In other words, destructuring can be (and often is) used directly for extracting parameters passed to a function. If you're familiar with React, you probably have seen this before!

const person = {
    name: 'Eddie',
    age: 24
};

function introduce({ name, age }) {
    console.log(`I'm ${name} and I'm ${age} years old!`);
}

introduce(person);
// "I'm Eddie and I'm 24 years old!"

Spread Syntax

A javascript concept that can throw people off but is relatively simple is the spread operator! In the following case, Math.max can't be applied to the arr array because it doesn't take an array as an argument, it takes the individual elements as arguments. The spread operator ... is used to pull the individual elements out the array.

const arr = [4, 6, -1, 3, 10, 4];
const max = Math.max(...arr);
console.log(max);
// 10

Rest Syntax

Let's talk about javascript rest syntax. You can use it to put any number of arguments passed to a function into an array!

function myFunc(...args) {
    console.log(args[0] + args[1]);
}

myFunc(1, 2, 3, 4);
// 3

Array Methods

JavaScript array methods can often provide you incredible, elegant ways to perform the data transformation you need. As a contributor to StackOverflow, I frequently see questions regarding how to manipulate an array of objects in one way or another. This tends to be the perfect use case for array methods.

I will cover a number of different array methods here, organized by similar methods that sometimes get conflated. This list is in no way comprehensive: I encourage you to review and practice all of them discussed on MDN (my favorite JavaScript reference).

map, filter, reduce

There is some confusion around the javascript array methods map, filter, reduce. These are helpful methods for transforming an array or returning an aggregate value.

  • map: return array where each element is transformed as specified by the function
const arr = [1, 2, 3, 4, 5, 6];
const mapped = arr.map(el => el + 20);
console.log(mapped);
// [21, 22, 23, 24, 25, 26]
  • filter: return array of elements where the function returns true
const arr = [1, 2, 3, 4, 5, 6];
const filtered = arr.filter(el => el === 2 || el === 4);
console.log(filtered);
// [2, 4]
  • reduce: accumulate values as specified in function
const arr = [1, 2, 3, 4, 5, 6];
const reduced = arr.reduce((total, current) => total + current, 0);
console.log(reduced);
// 21

Note: It is always advised to specify an initialValue or you could receive an error. For example:

const arr = [];
const reduced = arr.reduce((total, current) => total + current);
console.log(reduced);
// Uncaught TypeError: Reduce of empty array with no initial value

Note: If there’s no initialValue, then reduce takes the first element of the array as the initialValue and starts the iteration from the 2nd element

You can also read this tweet by Sophie Alpert (@sophiebits), when it is recommended to use reduce

find, findIndex, indexOf

The array methods find, findIndex, and indexOf can often be conflated. Use them as follows.

  • find: return the first instance that matches the specified criteria. Does not progress to find any other matching instances.
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const found = arr.find(el => el > 5);
console.log(found);
// 6

Again, note that while everything after 5 meets the criteria, only the first matching element is returned. This is actually super helpful in situations where you would normally break a for loop when you find a match!

  • findIndex: This works almost identically to find, but rather than returning the first matching element it returns the index of the first matching element. Take the following example, which uses names instead of numbers for clarity.
const arr = ['Nick', 'Frank', 'Joe', 'Frank'];
const foundIndex = arr.findIndex(el => el === 'Frank');
console.log(foundIndex);
// 1
  • indexOf: Works almost identically to findIndex, but instead of taking a function as an argument it takes a simple value. You can use this when you have simpler logic and don't need to use a function to check whether there is a match.
const arr = ['Nick', 'Frank', 'Joe', 'Frank'];
const foundIndex = arr.indexOf('Frank');
console.log(foundIndex);
// 1

push, pop, shift, unshift

There are a lot of great array method to help add or remove elements from arrays in a targeted fashion.

  • push: This is a relatively simple method that adds an item to the end of an array. It modifies the array in-place and the function itself returns the length of the new array.
const arr = [1, 2, 3, 4];
const pushed = arr.push(5);
console.log(arr);
// [1, 2, 3, 4, 5]
console.log(pushed);
// 5
  • pop: This removes the last item from an array. Again, it modifies the array in place. The function itself returns the item removed from the array.
const arr = [1, 2, 3, 4];
const popped = arr.pop();
console.log(arr);
// [1, 2, 3]
console.log(popped);
// 4
  • shift: This removes the first item from an array. Again, it modifies the array in place. The function itself returns the item removed from the array.
const arr = [1, 2, 3, 4];
const shifted = arr.shift();
console.log(arr);
// [2, 3, 4]
console.log(shifted);
// 1
  • unshift: This adds one or more elements to the beginning of an array. Again, it modifies the array in place. Unlike a lot of the other methods, the function itself returns the new length of the array.
const arr = [1, 2, 3, 4];
const unshifted = arr.unshift(5, 6, 7);
console.log(arr);
// [5, 6, 7, 1, 2, 3, 4]
console.log(unshifted);
// 7

splice, slice

These methods either modify or return subsets of arrays.

  • splice: Change the contents of an array by removing or replacing existing elements and/or adding new elements. This method modifies the array in place.
The following code sample can be read as: at position 1 of the array, remove 0 elements and insert b.
const arr = ['a', 'c', 'd', 'e'];
arr.splice(1, 0, 'b');
console.log(arr);
// ['a', 'b', 'c', 'd', 'e']
  • slice: returns a shallow copy of an array from a specified start position and before a specified end position. If no end position is specified, the rest of the array is returned. Importantly, this method does not modify the array in place but rather returns the desired subset.
const arr = ['a', 'b', 'c', 'd', 'e'];
const sliced = arr.slice(2, 4);
console.log(sliced);
// ['c', 'd']
console.log(arr);
// ['a', 'b', 'c', 'd', 'e']

sort

  • sort: sorts an array based on the provided function which takes a first element and second element argument. Modifies the array in place. If the function returns negative or 0, the order remains unchanged. If positive, the element order is switched.
const arr = [1, 7, 3, -1, 5, 7, 2];
const sorter = (firstEl, secondEl) => firstEl - secondEl;
arr.sort(sorter);
console.log(arr);
// [-1, 1, 2, 3, 5, 7, 7]

Phew, did you catch all of that? Neither did I. In fact, I had to reference the MDN docs a lot while writing this - and that's okay! Just knowing what kind of methods are out there with get you 95% of the way there.

Generators

Don't fear the *. The generator function specifies what value is yielded next time next() is called. Can either have a finite number of yields, after which next() returns an undefined value, or an infinite number of values using a loop.

function* greeter() {
    yield 'Hi';
    yield 'How are you?';
    yield 'Bye';
}

const greet = greeter();

console.log(greet.next().value);
// 'Hi'
console.log(greet.next().value);
// 'How are you?'
console.log(greet.next().value);
// 'Bye'
console.log(greet.next().value);
// undefined

And using a generator for infinite values:

function* idCreator() {
    let i = 0;
    while (true) yield i++;
}

const ids = idCreator();

console.log(ids.next().value);
// 0
console.log(ids.next().value);
// 1
console.log(ids.next().value);
// 2
// etc...

Identity Operator (===) vs. Equality Operator (==)

Be sure to know the difference between the identify operator (===) and equality operator (==) in javascript! The == operator will do type conversion prior to comparing values whereas the === operator will not do any type conversion before comparing.

console.log(0 == '0');
// true
console.log(0 === '0');
// false

Object Comparison

A mistake I see javascript newcomers make is directly comparing objects. Variables are pointing to references to the objects in memory, not the objects themselves! One method to actually compare them is converting the objects to JSON strings. This has a drawback though: JSON.stringify is not able to stringify a lot of object types (e.g., functions and sets)! A safer way to compare objects is to pull in a library that specializes in deep object comparison (e.g., lodash's isEqual).

The following objects appear equal but they are in fact pointing to different references.

const joe1 = { name: 'Joe' };
const joe2 = { name: 'Joe' };

console.log(joe1 === joe2);
// false

Conversely, the following evaluates as true because one object is set equal to the other object and are therefore pointing to the same reference (there is only one object in memory).

const joe1 = { name: 'Joe' };
const joe2 = joe1;

console.log(joe1 === joe2);
// true

Make sure to review the Value vs. Reference section above to fully understand the ramifications of setting a variable equal to another variable that's pointing to a reference to an object in memory!

Callback Functions

Far too many people are intimidated by javascript callback functions! They are simple, take this example. The console.log function is being passed as a callback to myFunc. It gets executed when setTimeout completes. That's all there is to it!

function myFunc(text, callback) {
    setTimeout(function() {
        callback(text);
    }, 2000);
}

myFunc('Hello world!', console.log);
// 'Hello world!'

Promises

Once you understand javascript callbacks you'll soon find yourself in nested "callback hell." This is where Promises help! Wrap your async logic in a Promise and resolve on success or reject on fail. Use then to handle success and catch to handle failure.

const myPromise = new Promise(function(res, rej) {
    setTimeout(function() {
        if (Math.random() < 0.9) {
            return res('Hooray!');
        }
        return rej('Oh no!');
    }, 1000);
});

myPromise
    .then(function(data) {
        console.log('Success: ' + data);
    })
    .catch(function(err) {
        console.log('Error: ' + err);
    });

// If Math.random() returns less than 0.9 the following is logged:
// "Success: Hooray!"
// If Math.random() returns 0.9 or greater the following is logged:
// "Error: Oh no!"

Avoid the nesting anti-pattern of promise chaining!

.then methods can be chained. I see a lot of new comers end up in some kind of call back hell inside of a promise when it's completely unnecessary.

//The wrong way
getSomedata.then(data => {
    getSomeMoreData(data).then(newData => {
        getSomeRelatedData(newData => {
            console.log(newData);
        });
    });
});
//The right way
getSomeData
    .then(data => {
        return getSomeMoreData(data);
    })
    .then(data => {
        return getSomeRelatedData(data);
    })
    .then(data => {
        console.log(data);
    });

You can see how it's much easier to read the second form and with ES6 implicit returns we could even simplify that further:

getSomeData
    .then(data => getSomeMoreData(data))
    .then(data => getSomeRelatedData(data))
    .then(data => console.log(data));

Because the function supplied to .then will be called with the the result of the resolve method from the promise we can omit the ceremony of creating an anonymous function altogether. This is equivalent to above:

getSomeData
    .then(getSomeMoreData)
    .then(getSomeRelatedData)
    .then(console.log);

Async Await

Once you get the hang of javascript promises, you might like async await, which is just "syntactic sugar" on top of promises. In the following example we create an async function and within that we await the greeter promise.

const greeter = new Promise((res, rej) => {
    setTimeout(() => res('Hello world!'), 2000);
});

async function myFunc() {
    const greeting = await greeter;
    console.log(greeting);
}

myFunc();
// 'Hello world!'

Async functions return a promise

One important thing to note here is that the result of an async function is a promise.

const greeter = new Promise((res, rej) => {
    setTimeout(() => res('Hello world!'), 2000);
});

async function myFunc() {
    return await greeter;
}

console.log(myFunc()); // => Promise {}

myFunc().then(console.log); // => Hello world!

DOM Manipulation

Create Your Own Query Selector Shorthand

When working with JS in the browser, instead of writing document.querySelector()/document.querySelectorAll() multiple times, you could do the following thing:

const $ = document.querySelector.bind(document);
const $$ = document.querySelectorAll.bind(document);

// Usage
const demo = $('#demo');
// Select all the `a` tags
[...$$("a[href *='#']")].forEach(console.log);

Interview Questions

Traversing a Linked List

Here's a javascript solution to a classic software development interview question: traversing a linked list. You can use a while loop to recursively iterate through the linked list until there are no more values!

const linkedList = {
    val: 5,
    next: {
        val: 3,
        next: {
            val: 10,
            next: null
        }
    }
};

const arr = [];
let head = linkedList;

while (head !== null) {
    arr.push(head.val);
    head = head.next;
}

console.log(arr);
// [5, 3, 10]

Miscellaneous

Increment and Decrement

Ever wonder what the difference between i++ and ++i was? Did you know both were options? i++ returns i and then increments it whereas ++i increments i and then returns it.

let i = 0;
console.log(i++);
// 0
let i = 0;
console.log(++i);
// 1

Contributing

Contributions welcome! All I ask is that you open an issue and we discuss your proposed changes before you create a pull request.


Want to learn more about JavaScript development? Consider:

  • Signing up for my free newsletter where I periodically send out digestible bits of JavaScript knowledge!
  • Subscribing to my free youtube channel where I teach JavaScript, Typescript, and React tutorials!

Author: nas5w
Source Code: https://github.com/nas5w/javascript-tips-and-tidbits 
License: MIT license

#javascript #tips 

javascript-tips-and-tidbits: A continuously-evolving compendium
Evan John

Evan John

1653567238

Google Web Stories: All That You Should Know

If you want expert services in creating and distributing Google Web Stories, then you can also take assistance from our SEO Company India. Else, you can get along and read this complete guide.

This blog covers Google Web Stories extensively. So, if you’re trying to learn more about Google Web Stories, this read will serve as a perfect guide for you. - https://www.ezrankings.org/blog/google-web-stories/

#GoogleWebStories #GoogleWebStory #Webstories #EZRankings #YourDigitalPartner #SEOCompany #SEOIndia #seoservices #digitalsolution #business #ezrankings #strating #strategy #tips 

Google Web Stories: All That You Should Know
Royce  Reinger

Royce Reinger

1649408100

Git-tips: Most Commonly Used Git Tips and Tricks

git-tips

Collection of git-tips, want to add your tips? Checkout contributing.md

Tools:

P.S: All these commands are tested on git version 2.7.4 (Apple Git-66).

 

   

Everyday Git in twenty commands or so

git help everyday

Show helpful guides that come with Git

git help -g

Search change by content

git log -S'<a term in the source>'

Show changes over time for specific file

git log -p <file_name>

Remove sensitive data from history, after a push

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <path-to-your-file>' --prune-empty --tag-name-filter cat -- --all && git push origin --force --all

Sync with remote, overwrite local changes

git fetch origin && git reset --hard origin/master && git clean -f -d

List of all files till a commit

git ls-tree --name-only -r <commit-ish>

Git reset first commit

git update-ref -d HEAD

Reset: preserve uncommitted local changes

git reset --keep <commit>

List all the conflicted files

git diff --name-only --diff-filter=U

List of all files changed in a commit

git diff-tree --no-commit-id --name-only -r <commit-ish>

Unstaged changes since last commit

git diff

Changes staged for commit

git diff --cached

Alternatives:

git diff --staged

Show both staged and unstaged changes

git diff HEAD

List all branches that are already merged into master

git branch --merged master

Quickly switch to the previous branch

git checkout -

Alternatives:

git checkout @{-1}

Remove branches that have already been merged with master

git branch --merged master | grep -v '^\*' | xargs -n 1 git branch -d

Alternatives:

git branch --merged master | grep -v '^\*\|  master' | xargs -n 1 git branch -d # will not delete master if master is not checked out

List all branches and their upstreams, as well as last commit on branch

git branch -vv

Track upstream branch

git branch -u origin/mybranch

Delete local branch

git branch -d <local_branchname>

Delete remote branch

git push origin --delete <remote_branchname>

Alternatives:

git push origin :<remote_branchname>
git branch -dr <remote/branch>

Create local tag

git tag <tag-name>

Delete local tag

git tag -d <tag-name>

Delete remote tag

git push origin :refs/tags/<tag-name>

Undo local changes with the last content in head

git checkout -- <file_name>

Revert: Undo a commit by creating a new commit

git revert <commit-ish>

Reset: Discard commits, advised for private branch

git reset <commit-ish>

Reword the previous commit message

git commit -v --amend

See commit history for just the current branch

git cherry -v master

Amend author.

git commit --amend --author='Author Name <email@address.com>'

Reset author, after author has been changed in the global config.

git commit --amend --reset-author --no-edit

Changing a remote's URL

git remote set-url origin <URL>

Get list of all remote references

git remote

Alternatives:

git remote show

Get list of all local and remote branches

git branch -a

Get only remote branches

git branch -r

Stage parts of a changed file, instead of the entire file

git add -p

Get git bash completion

curl -L http://git.io/vfhol > ~/.git-completion.bash && echo '[ -f ~/.git-completion.bash ] && . ~/.git-completion.bash' >> ~/.bashrc

What changed since two weeks?

git log --no-merges --raw --since='2 weeks ago'

Alternatives:

git whatchanged --since='2 weeks ago'

See all commits made since forking from master

git log --no-merges --stat --reverse master..

Pick commits across branches using cherry-pick

git checkout <branch-name> && git cherry-pick <commit-ish>

Find out branches containing commit-hash

git branch -a --contains <commit-ish>

Alternatives:

git branch --contains <commit-ish>

Git Aliases

git config --global alias.<handle> <command> 
git config --global alias.st status

Saving current state of tracked files without commiting

git stash

Alternatives:

git stash push

Saving current state of unstaged changes to tracked files

git stash -k

Alternatives:

git stash --keep-index
git stash push --keep-index

Saving current state including untracked files

git stash -u

Alternatives:

git stash push -u
git stash push --include-untracked

Saving current state with message

git stash push -m <message>

Alternatives:

git stash push --message <message>

Saving current state of all files (ignored, untracked, and tracked)

git stash -a

Alternatives:

git stash --all
git stash push --all

Show list of all saved stashes

git stash list

Show the contents of any stash in patch form

git stash show -p <stash@{n}>

Apply any stash without deleting from the stashed list

git stash apply <stash@{n}>

Apply last stashed state and delete it from stashed list

git stash pop

Alternatives:

git stash apply stash@{0} && git stash drop stash@{0}

Delete all stored stashes

git stash clear

Alternatives:

git stash drop <stash@{n}>

Grab a single file from a stash

git checkout <stash@{n}> -- <file_path>

Alternatives:

git checkout stash@{0} -- <file_path>

Show all tracked files

git ls-files -t

Show all untracked files

git ls-files --others

Show all ignored files

git ls-files --others -i --exclude-standard

Create new working tree from a repository (git 2.5)

git worktree add -b <branch-name> <path> <start-point>

Create new working tree from HEAD state

git worktree add --detach <path> HEAD

Untrack files without deleting

git rm --cached <file_path>

Alternatives:

git rm --cached -r <directory_path>

Before deleting untracked files/directory, do a dry run to get the list of these files/directories

git clean -n

Forcefully remove untracked files

git clean -f

Forcefully remove untracked directory

git clean -f -d

Update all the submodules

git submodule foreach git pull

Alternatives:

git submodule update --init --recursive
git submodule update --remote

Show all commits in the current branch yet to be merged to master

git cherry -v master

Alternatives:

git cherry -v master <branch-to-be-merged>

Rename a branch

git branch -m <new-branch-name>

Alternatives:

git branch -m [<old-branch-name>] <new-branch-name>

Rebases 'feature' to 'master' and merges it in to master

git rebase master feature && git checkout master && git merge -

Archive the master branch

git archive master --format=zip --output=master.zip

Modify previous commit without modifying the commit message

git add --all && git commit --amend --no-edit

Prunes references to remove branches that have been deleted in the remote.

git fetch -p

Alternatives:

git remote prune origin

Delete local branches that has been squash and merged in the remote.

git branch -vv | grep ': gone]' | awk '{print <!-- @doxie.inject start -->}' | xargs git branch -D

Retrieve the commit hash of the initial revision.

 git rev-list --reverse HEAD | head -1

Alternatives:

git rev-list --max-parents=0 HEAD
git log --pretty=oneline | tail -1 | cut -c 1-40
git log --pretty=oneline --reverse | head -1 | cut -c 1-40

Visualize the version tree.

git log --pretty=oneline --graph --decorate --all

Alternatives:

gitk --all
git log --graph --pretty=format:'%C(auto) %h | %s | %an | %ar%d'

Visualize the tree including commits that are only referenced from reflogs

git log --graph --decorate --oneline $(git rev-list --walk-reflogs --all)

Deploying git tracked subfolder to gh-pages

git subtree push --prefix subfolder_name origin gh-pages

Adding a project to repo using subtree

git subtree add --prefix=<directory_name>/<project_name> --squash git@github.com:<username>/<project_name>.git master

Get latest changes in your repo for a linked project using subtree

git subtree pull --prefix=<directory_name>/<project_name> --squash git@github.com:<username>/<project_name>.git master

Export a branch with history to a file.

git bundle create <file> <branch-name>

Import from a bundle

git clone repo.bundle <repo-dir> -b <branch-name>

Get the name of current branch.

git rev-parse --abbrev-ref HEAD

Ignore one file on commit (e.g. Changelog).

git update-index --assume-unchanged Changelog; git commit -a; git update-index --no-assume-unchanged Changelog

Stash changes before rebasing

git rebase --autostash

Fetch pull request by ID to a local branch

git fetch origin pull/<id>/head:<branch-name>

Alternatives:

git pull origin pull/<id>/head:<branch-name>

Show the most recent tag on the current branch.

git describe --tags --abbrev=0

Show inline word diff.

git diff --word-diff

Show changes using common diff tools.

git difftool [-t <tool>] <commit1> <commit2> <path>

Don’t consider changes for tracked file.

git update-index --assume-unchanged <file_name>

Undo assume-unchanged.

git update-index --no-assume-unchanged <file_name>

Clean the files from .gitignore.

git clean -X -f

Restore deleted file.

git checkout <deleting_commit> -- <file_path>

Restore file to a specific commit-hash

git checkout <commit-ish> -- <file_path>

Always rebase instead of merge on pull.

git config --global pull.rebase true

Alternatives:

#git < 1.7.9
git config --global branch.autosetuprebase always

List all the alias and configs.

git config --list

Make git case sensitive.

git config --global core.ignorecase false

Add custom editors.

git config --global core.editor '$EDITOR'

Auto correct typos.

git config --global help.autocorrect 1

Check if the change was a part of a release.

git name-rev --name-only <SHA-1>

Dry run. (any command that supports dry-run flag should do.)

git clean -fd --dry-run

Marks your commit as a fix of a previous commit.

git commit --fixup <SHA-1>

Squash fixup commits normal commits.

git rebase -i --autosquash

Skip staging area during commit.

git commit --only <file_path>

Interactive staging.

git add -i

List ignored files.

git check-ignore *

Status of ignored files.

git status --ignored

Commits in Branch1 that are not in Branch2

git log Branch1 ^Branch2

List n last commits

git log -<n>

Alternatives:

git log -n <n>

Reuse recorded resolution, record and reuse previous conflicts resolutions.

git config --global rerere.enabled 1

Open all conflicted files in an editor.

git diff --name-only | uniq | xargs $EDITOR

Count unpacked number of objects and their disk consumption.

git count-objects --human-readable

Prune all unreachable objects from the object database.

git gc --prune=now --aggressive

Instantly browse your working repository in gitweb.

git instaweb [--local] [--httpd=<httpd>] [--port=<port>] [--browser=<browser>]

View the GPG signatures in the commit log

git log --show-signature

Remove entry in the global config.

git config --global --unset <entry-name>

Checkout a new branch without any history

git checkout --orphan <branch_name>

Extract file from another branch.

git show <branch_name>:<file_name>

List only the root and merge commits.

git log --first-parent

Change previous two commits with an interactive rebase.

git rebase --interactive HEAD~2

List all branch is WIP

git checkout master && git branch --no-merged

Find guilty with binary search

git bisect start                    # Search start 
git bisect bad                      # Set point to bad commit 
git bisect good v2.6.13-rc2         # Set point to good commit|tag 
git bisect bad                      # Say current state is bad 
git bisect good                     # Say current state is good 
git bisect reset                    # Finish search 

Bypass pre-commit and commit-msg githooks

git commit --no-verify

List commits and changes to a specific file (even through renaming)

git log --follow -p -- <file_path>

Clone a single branch

git clone -b <branch-name> --single-branch https://github.com/user/repo.git

Create and switch new branch

git checkout -b <branch-name>

Alternatives:

git branch <branch-name> && git checkout <branch-name>
git switch -c <branch-name>

Ignore file mode changes on commits

git config core.fileMode false

Turn off git colored terminal output

git config --global color.ui false

Specific color settings

git config --global <specific command e.g branch, diff> <true, false or always>

Show all local branches ordered by recent commits

git for-each-ref --sort=-committerdate --format='%(refname:short)' refs/heads/

Find lines matching the pattern (regex or string) in tracked files

git grep --heading --line-number 'foo bar'

Clone a shallow copy of a repository

git clone https://github.com/user/repo.git --depth 1

Search Commit log across all branches for given text

git log --all --grep='<given-text>'

Get first commit in a branch (from master)

git log --oneline master..<branch-name> | tail -1

Alternatives:

git log --reverse master..<branch-name> | head -6

Unstaging Staged file

git reset HEAD <file-name>

Force push to Remote Repository

git push -f <remote-name> <branch-name>

Adding Remote name

git remote add <remote-nickname> <remote-url>

List all currently configured remotes

git remote -v

Show the author, time and last revision made to each line of a given file

git blame <file-name>

Group commits by authors and title

git shortlog

Forced push but still ensure you don't overwrite other's work

git push --force-with-lease <remote-name> <branch-name>

Show how many lines does an author contribute

git log --author='_Your_Name_Here_' --pretty=tformat: --numstat | gawk '{ add += <!-- @doxie.inject start -->; subs += <!-- @doxie.inject end -->; loc += <!-- @doxie.inject start --> - <!-- @doxie.inject end --> } END { printf "added lines: %s removed lines: %s total lines: %s
", add, subs, loc }' -

Alternatives:

git log --author='_Your_Name_Here_' --pretty=tformat: --numstat | awk '{ add += <!-- @doxie.inject start -->; subs += <!-- @doxie.inject end -->; loc += <!-- @doxie.inject start --> - <!-- @doxie.inject end --> } END { printf "added lines: %s, removed lines: %s, total lines: %s
", add, subs, loc }' - # on Mac OSX

Revert: Reverting an entire merge

git revert -m 1 <commit-ish>

Number of commits in a branch

git rev-list --count <branch-name>

Alias: git undo

git config --global alias.undo '!f() { git reset --hard $(git rev-parse --abbrev-ref HEAD)@{${1-1}}; }; f'

Add object notes

git notes add -m 'Note on the previous commit....'

Show all the git-notes

git log --show-notes='*'

Apply commit from another repository

git --git-dir=<source-dir>/.git format-patch -k -1 --stdout <SHA1> | git am -3 -k

Specific fetch reference

git fetch origin master:refs/remotes/origin/mymaster

Find common ancestor of two branches

git merge-base <branch-name> <other-branch-name>

List unpushed git commits

git log --branches --not --remotes

Alternatives:

git log @{u}..
git cherry -v

Add everything, but whitespace changes

git diff --ignore-all-space | git apply --cached

Edit [local/global] git config

git config [--global] --edit

blame on certain range

git blame -L <start>,<end>

Show a Git logical variable.

git var -l | <variable>

Preformatted patch file.

git format-patch -M upstream..topic

Get the repo name.

git rev-parse --show-toplevel

logs between date range

git log --since='FEB 1 2017' --until='FEB 14 2017'

Exclude author from logs

git log --perl-regexp --author='^((?!excluded-author-regex).*)

Generates a summary of pending changes

git request-pull v1.0 https://git.ko.xz/project master:for-linus

List references in a remote repository

git ls-remote git://git.kernel.org/pub/scm/git/git.git

Backup untracked files.

git ls-files --others -i --exclude-standard | xargs zip untracked.zip

List all git aliases

git config -l | grep alias | sed 's/^alias\.//g'

Alternatives:

git config -l | grep alias | cut -d '.' -f 2

Show git status short

git status --short --branch

Checkout a commit prior to a day ago

git checkout master@{yesterday}

Push the current branch to the same name on the remote repository

git push origin HEAD

Push a new local branch to remote repository and track

git push -u origin <branch_name>

Change a branch base

git rebase --onto <new_base> <old_base>

Use SSH instead of HTTPs for remotes

git config --global url.'git@github.com:'.insteadOf 'https://github.com/'

Update a submodule to the latest commit

cd <path-to-submodule>
git pull origin <branch>
cd <root-of-your-main-project>
git add <path-to-submodule>
git commit -m "submodule updated"

Prevent auto replacing LF with CRLF

git config --global core.autocrlf false

 English | 中文 | Русский | 한국어 | Tiếng Việt | 日本語 | नेपाली | Polski | فارسی

Author: Git-tips
Source Code: https://github.com/git-tips/tips 
License: MIT License

#git #tips #tricks 

Git-tips: Most Commonly Used Git Tips and Tricks

Cómo Usar Google: Consejos De Búsqueda Para Obtener Mejores Resultados

Google es la herramienta más poderosa del mundo si sabes cómo usarla correctamente. Déjame mostrarte cómo mejorar en Google.

1. Use comillas para la búsqueda de coincidencia exacta

Puede usar comillas para forzar una búsqueda de coincidencia exacta. Esto es muy útil si sabe exactamente la frase de búsqueda que está buscando. Simplemente obtendrá resultados más precisos si hace esto.

"what is javascript"

Ejemplo de coincidencia exacta de búsqueda de Google

2. Utilice el operador AND

El operador AND devolverá solo resultados relacionados con ambos términos. En el siguiente ejemplo, puede ver el caso de uso perfecto de este operador.

html AND css

Búsqueda de Google con la palabra clave AND

3. Usa el operador OR

Puede utilizar el operador OR para obtener resultados relacionados con uno de los términos de búsqueda que introduzca.

(javascript OR python) free course

Búsqueda de Google O ejemplo de palabra clave

4. Utilice el operador - (menos)

El operador - excluirá los resultados que contengan un término o frase. Entonces, en nuestro caso, queremos obtener resultados relacionados con JavaScript, pero excluir cualquier resultado de CSS de ellos.

javascript -css

Búsqueda de Google con - ejemplo de operador

5. Use comodines (*) como marcadores de posición

Puede usar los comodines (*) como marcadores de posición, que serán reemplazados por cualquier palabra o frase.

Éste es mi favorito. Lo uso cuando sé casi toda la frase de búsqueda y simplemente reemplazo la parte que no sé con un asterisco. Súper útil.

"how to start * in 6 months"

Búsqueda de Google con ejemplo de comodín

6. Cómo buscar dentro de un solo sitio web

Este es otro que uso con frecuencia. Si estoy buscando algo en particular en un sitio web específico, este es mi enfoque.

site:freecodecamp.org

Búsqueda de Google dentro de un solo ejemplo de sitio web

7. Cómo encontrar un tipo de archivo específico

También puede usar esta característica muy útil para ayudarlo a encontrar un tipo de archivo específico. Es útil si está buscando algunos materiales como archivos PDF. Si no lo has usado antes, a partir de ahora lo harás.

filetype:pdf learn css

Búsqueda en Google de un tipo de archivo específico

8. Cómo buscar un rango de números

Esto puede ser cualquier cosa. Si está buscando comprar algo que se encuentre dentro de un rango de precios específico, o si está buscando resultados que incluyan un rango específico de años, esto es para usted.

ecmascript 2016..2018

Búsqueda en Google de un rango específico de números

9. Usa el beforeOperador

Utilice el operador anterior para devolver solo los resultados anteriores a una fecha determinada.
Debe proporcionar fechas año-mes-día o solo un año. Por ejemplo:

javascript before:2020

Búsqueda de Google con operador antes

10. Usa el afterOperador

Utilice el operador after para devolver solo los resultados posteriores a una fecha determinada. Debe proporcionar fechas año-mes-día o solo un año. Súper útil si desea excluir resultados obsoletos.

web development after:2020

Búsqueda de Google con operador posterior

Como puede ver, Google puede ser una herramienta poderosa si sabe cómo usarla correctamente. 

Fuente: https://www.freecodecamp.org/news/use-google-search-tips/

#google #tips #search 

Cómo Usar Google: Consejos De Búsqueda Para Obtener Mejores Resultados

Googleの使い方–より良い結果を得るための検索のヒント

グーグルは、それを適切に使用する方法を知っていれば、世界で最も強力なツールです。グーグルで上手になる方法をお見せしましょう。

1.完全一致検索に引用符を使用する

引用符を使用して、完全一致検索を強制できます。これは、探している検索フレーズを正確に知っている場合に非常に便利です。これを行うと、より正確な結果が得られます。

"what is javascript"

Google検索の完全一致の例

2.AND演算子を使用します

AND演算子は、両方の用語に関連する結果のみを返します。以下の例では、この演算子の完全なユースケースを見ることができます。

html AND css

ANDキーワードでGoogle検索

3.OR演算子を使用します

OR演算子を使用して、入力した検索用語の1つに関連する結果を取得できます。

(javascript OR python) free course

Google検索またはキーワードの例

4.-(マイナス)演算子を使用します

-演算子は、用語または句を含む結果を除外します。したがって、この場合、JavaScript関連の結果を取得したいのですが、CSSの結果は除外します。

javascript -css

Google検索-演算子の例

5.プレースホルダーとして(*)ワイルドカードを使用します

(*)ワイルドカードをプレースホルダーとして使用できます。これは、任意の単語またはフレーズに置き換えられます。

これは私のお気に入りです。検索フレーズのほぼ全体を知っているときに使用し、わからない部分をアスタリスクに置き換えます。とても便利です。

"how to start * in 6 months"

ワイルドカードの例を使用したGoogle検索

6.単一のWebサイト内を検索する方法

これも私がよく使うものです。特定のウェブサイトで特に何かを検索している場合、これが私のアプローチです。

site:freecodecamp.org

単一のウェブサイトの例の中のグーグル検索

7.特定のファイルタイプを見つける方法

この非常に便利な機能を使用して、特定のファイルタイプを見つけることもできます。PDFのような資料を探している場合に便利です。これまで使用したことがない場合は、今後使用します。

filetype:pdf learn css

特定のファイルタイプをGoogleで検索

8.数字の範囲を検索する方法

これは何でもかまいません。特定の価格帯に該当するものを購入する場合、 または特定の年数を含む結果を検索する場合は、これが最適です。

ecmascript 2016..2018

Googleは特定の範囲の数字を検索します

9.before演算子を使用する

before演算子を使用して、指定された日付より前の結果のみを返します。
年、月、日の日付、または1年のみを指定する必要があります。例えば:

javascript before:2020

演算子の前でGoogle検索

10.after演算子を使用する

after演算子を使用して、指定された日付以降の結果のみを返します。年、月、日の日付、または1年のみを指定する必要があります。古い結果を除外したい場合に非常に便利です。

web development after:2020

アフター演算子を使用したGoogle検索

ご覧のとおり、Googleは、適切な使用方法を知っていれば、強力なツールになる可能性があります。 

出典:https ://www.freecodecamp.org/news/use-google-search-tips/

#google #tips #search 

Googleの使い方–より良い結果を得るための検索のヒント