Author Archives: Michael Duquette

REST – INSOMNIA(c)

This week let’s take another peek at the FoodKeeper API. We’ll review the endpoints and how to submit a request to retrieve data. Before we jump into this you need to download and install a REST client. I highly recommend Insomnia, the app not the sleep disorder, (download it here) I’ll wait… Now that you have Insomnia installed let’s talk about endpoints. Last week we covered the four primary HTTP verbs used with REST. Did you have a chance to visit https://www.restapitutorial.com despite the name of the website these aren’t tutorials on how to make REST calls but more like development guidelines for developers writing REST API’s.

So why did I recommend it? Because understanding how a good RESTful API is designed will make it easier to craft your REST calls. So the 4 basic HTTP verbs (POST, GET, PUT and DELETE) are CRUD (Create, Read, Update, Delete) operations. · POST = Create · GET = Read · PUT = Update · DELETE = Delete Kind of makes sense, right? These also make up the endpoints you access with your REST client. In JAVA we add an annotation to the class and the getter and setter methods to define the endpoint. For the FoodKeeper API we are using the Spring Framework (https://spring.io/) to help setup the Rest components. After all the imports are declared the class must be annotated with @RestController so that the compiler knows how to process it. Each endpoint must be annotated to reflect what it does. A POST endpoint would be annotated like this: @PostMapping(“/list/new”) The part in parenthesis indicates the URL path that you would pass as part of the REST Client call and is the actual endpoint. Let’s look at an online example. In a new tab on your browser go to: http://dummy.restapiexample.com/ The site lists 5 public endpoints you can practice against: · GET /employee · GET /employee/{id} · POST /create · PUT /update/{id} · DELETE /delete/{id} Note the curly braces with id in between them – this indicates that you will be passing a value.
Open your REST client, in Insomnia you would type in the URL in the White field in the top center of the screen and select the REST Method to the left of it: Click the Send button and watch the Right hand column you will see it populate with employees. This is the first entry in the list that was returned for me: Play around and get familiar with your REST Client and hit some of the other endpoints at

http://dummy.restapiexample.com/ TIP: For the endpoints above with {id} just append the url string with a number like this: http://dummy.restapiexample.com/api/v1/employees/6821 All right go play and learn something! #CS@Worcester #CS448

From the blog Home | Michael Duquette by Michael Duquette and used with permission of the author. All other rights reserved by the author.

SOLID – Like a (Open/Closed) Rock

TLDR: your code should be open for extension but closed for modification. o Keep the current functions, Classes, modules immutable o Extend, in a composable way (try to avoid inheritance as it tends to couple things) Read the full article about the Open/Closed Principle from CodeBurst here The main idea is that in one part of your code you have your abstractions such as classes that do one thing and do it well. You don’t want to be modifying them and should be keeping them clean and cohesive. You ready for this? The Open/Closed Principle = COMPOSITE DESIGN PATTERN – mind blown! How about that, we’ve come full circle back to design patterns. Now… where’s that duck? Don’t get your beaks out of joint, no ducks this time we are going to be working with coffee machines instead. I really enjoy a great cup of coffee! This article over on Stackify has a great code example of applying the Open/Closed Principle to a CoffeeMachine app. To summarize: You have a BasicCoffeeMachine Class it has a constructor, a public method to add ground coffee, and a public method that brews a filter coffee. A BasicCoffeeApp class that prepares a HashMap with ground coffee, instantiates a BasicCoffeeMachine object, and calls the prepareCoffee method to brew the coffee. Pretty simple right? Our app brews us a fresh pot of filter coffee. Well what if we upgrade our pot to include an integrated grinder? Now we can brew espresso also but our CoffeeApp doesn’t support this kind of brewer. Sounds like some code changes to the CoffeApp are in order. Why don’t we also update it so that we don’t need to adapt to future types of coffee machines. So how do we do this? Well, let’s start off by extracting what we can to create an interface. Let’s pull out what’s mandatory for controlling a coffee machine, but none of the optional stuff. Since the only real “control” in the BasicCoffeeMachine is the brewCoffee method we can put that up in an interface and have BasicCoffeeMachine implement the interface (CoffeeMachine). Now we can add more implementations of CoffeeMachine. See what we’ve done there? Just by adding the interface and implementing more variations of the CoffeeMachine we’ve adopted the Open/Closed Principle. So, what’s next? We’re not done refactoring, we need to modify the CoffeeApp to make use of the CoffeMachine interface by instantiating instances of it for the different coffee types. Make mine a tall black with a shot of vanilla and please go check out the code example and the full article here. #CS@Worcester #CS343

From the blog Home | Michael Duquette by Michael Duquette and used with permission of the author. All other rights reserved by the author.

SOLID – Like a (Open/Closed) Rock

TLDR: your code should be open for extension but closed for modification. o Keep the current functions, Classes, modules immutable o Extend, in a composable way (try to avoid inheritance as it tends to couple things) Read the full article about the Open/Closed Principle from CodeBurst here The main idea is that in one part of your code you have your abstractions such as classes that do one thing and do it well. You don’t want to be modifying them and should be keeping them clean and cohesive. You ready for this? The Open/Closed Principle = COMPOSITE DESIGN PATTERN – mind blown! How about that, we’ve come full circle back to design patterns. Now… where’s that duck? Don’t get your beaks out of joint, no ducks this time we are going to be working with coffee machines instead. I really enjoy a great cup of coffee! This article over on Stackify has a great code example of applying the Open/Closed Principle to a CoffeeMachine app. To summarize: You have a BasicCoffeeMachine Class it has a constructor, a public method to add ground coffee, and a public method that brews a filter coffee. A BasicCoffeeApp class that prepares a HashMap with ground coffee, instantiates a BasicCoffeeMachine object, and calls the prepareCoffee method to brew the coffee. Pretty simple right? Our app brews us a fresh pot of filter coffee. Well what if we upgrade our pot to include an integrated grinder? Now we can brew espresso also but our CoffeeApp doesn’t support this kind of brewer. Sounds like some code changes to the CoffeApp are in order. Why don’t we also update it so that we don’t need to adapt to future types of coffee machines. So how do we do this? Well, let’s start off by extracting what we can to create an interface. Let’s pull out what’s mandatory for controlling a coffee machine, but none of the optional stuff. Since the only real “control” in the BasicCoffeeMachine is the brewCoffee method we can put that up in an interface and have BasicCoffeeMachine implement the interface (CoffeeMachine). Now we can add more implementations of CoffeeMachine. See what we’ve done there? Just by adding the interface and implementing more variations of the CoffeeMachine we’ve adopted the Open/Closed Principle. So, what’s next? We’re not done refactoring, we need to modify the CoffeeApp to make use of the CoffeMachine interface by instantiating instances of it for the different coffee types. Make mine a tall black with a shot of vanilla and please go check out the code example and the full article here. #CS@Worcester #CS343

From the blog Michael Duquette by Michael Duquette and used with permission of the author. All other rights reserved by the author.

Gradle: Test test

Testing Testing Testing… A few weeks ago we focused on JUnit testing using Gradle. I thought I would share a few things I learned along the way getting my projects setup for JUnit testing with Gradle. Since our testing is centered around Jupiter (JUnit5) there are a few unique things you need to do to get Gradle to behave as expected. If you are using Jupiter for your JUnit testing you need to have Gradle version 4.6 or later installed. So let’s start there. Verify the version of Gradle you have installed: Open a bash shell in your projects root directory and run: ./gradlew –version If you are not running a version greater than 4.6 update to the latest version before proceeding. Let’s setup our project to use Gradle. Open a bash shell in your projects root folder and run this: gradle init –type java-library –dsl groovy –test-framework junit This tells Gradle that we are creating a new JAVA project and that we will be testing with JUnit. It will take a few seconds to run and once complete you should see a message that says: BUILD SUCCESSFUL in xxseconds 2 actionable tasks: 2 executed Now check out your project folder. You will now see 3 new folders: gradle .gradle src and the following new files: .gitignore build.gradle gradlew gradlew.bat settings.gradle We are going to start off making changes to the build.gradle file. Using your favorite editor (I prefer Notepad++)open build.gradle and verify that the following is your frist entry following the commented docs: 1 plugins { 2 // Apply the java-library plugin to add support for Java Library 3 id ‘java-library’ 4 } This tells Gradle that it is going to be building a JAVA program. Now we need to make sure that Gradle gets the required and dependencies so add the following to build.gradle: 20 dependencies { 21 // This dependency is exported to consumers, that is to say found on their compile classpath. 22 api ‘org.apache.commons:commons-math3:3.6.1’ 24 // This dependency is used internally, and not exposed to consumers on their own compile classpath. 25 implementation ‘com.google.guava:guava:27.0.1-jre’ 26 // Use JUnit test framework 27 testImplementation ‘junit:junit:4.12’ 28 testImplementation ‘org.junit.jupiter:junit-jupiter-api:5.5.0-M1’ 29 testRuntimeOnly ‘org.junit.jupiter:junit-jupiter-engine:5.5.0-M1’ 30 } We tell build.gradle which frameworks to include for the JUnit testing. Jupiter is backwards compatible but if we want to run any JUnit 4 test we include that junit:4.12 dependency. This just ensure the correct flavor of JUnit is used for the testing. Now we’ll add one more line to our build.gradle to make sure we enable Gradle’s native JUnit 5 support. Add the following lines after the dependencies: 33 test { 34 useJUnitPlatform() 35 } Now hop back into your IDE and work on your project. Save all of your changes and navigate back to your project folder. Open up the src folder. You will see the following sub-folders: main test Both of these contain a folder called java. You will move your JAVA files into the java folders in the main and test subfolders. EXAMPLE: Let’s say I am working a project that has Duck.java , Pond.java , and DuckTest.java. Both Duck.java and Pond.java should be moved to ../src/main/java and DuckTest.java would be moved to ../src/test/java Once you’ve moved your files into the correct location run this in your bash shell: gradle build Once this succeeds run this in your bash shell: gradle test Once this finishes and you get a success message navigate to your project folder and go to /build/reports/tests/test/ and open up the index.html. This will give you a breakdown of how your gradle test went. Now that you’ve sucessfully setup Gradle you need to go back into your IDE and clean up your projects paths so that you are working in the src/main/java folder and the src/test/java folder. See easey peasey lemon squeezy! Next week we’ll go over how to intigrate our projects with GitLab so that GitLab does the testing for us. In the meantime checkout the docs up on Gradle.org related to testing with JAVA & JVM: https://docs.gradle.org/5.2.1/userguide/java_testing.html#using_junit5 #CS@Worcester #CS443

From the blog Home | Michael Duquette by Michael Duquette and used with permission of the author. All other rights reserved by the author.

Gradle: Test test

Testing Testing Testing… A few weeks ago we focused on JUnit testing using Gradle. I thought I would share a few things I learned along the way getting my projects setup for JUnit testing with Gradle. Since our testing is centered around Jupiter (JUnit5) there are a few unique things you need to do to get Gradle to behave as expected. If you are using Jupiter for your JUnit testing you need to have Gradle version 4.6 or later installed. So let’s start there. Verify the version of Gradle you have installed: Open a bash shell in your projects root directory and run: ./gradlew –version If you are not running a version greater than 4.6 update to the latest version before proceeding. Let’s setup our project to use Gradle. Open a bash shell in your projects root folder and run this: gradle init –type java-library –dsl groovy –test-framework junit This tells Gradle that we are creating a new JAVA project and that we will be testing with JUnit. It will take a few seconds to run and once complete you should see a message that says: BUILD SUCCESSFUL in xxseconds 2 actionable tasks: 2 executed Now check out your project folder. You will now see 3 new folders: gradle .gradle src and the following new files: .gitignore build.gradle gradlew gradlew.bat settings.gradle We are going to start off making changes to the build.gradle file. Using your favorite editor (I prefer Notepad++)open build.gradle and verify that the following is your frist entry following the commented docs: 1 plugins { 2 // Apply the java-library plugin to add support for Java Library 3 id ‘java-library’ 4 } This tells Gradle that it is going to be building a JAVA program. Now we need to make sure that Gradle gets the required and dependencies so add the following to build.gradle: 20 dependencies { 21 // This dependency is exported to consumers, that is to say found on their compile classpath. 22 api ‘org.apache.commons:commons-math3:3.6.1’ 24 // This dependency is used internally, and not exposed to consumers on their own compile classpath. 25 implementation ‘com.google.guava:guava:27.0.1-jre’ 26 // Use JUnit test framework 27 testImplementation ‘junit:junit:4.12’ 28 testImplementation ‘org.junit.jupiter:junit-jupiter-api:5.5.0-M1’ 29 testRuntimeOnly ‘org.junit.jupiter:junit-jupiter-engine:5.5.0-M1’ 30 } We tell build.gradle which frameworks to include for the JUnit testing. Jupiter is backwards compatible but if we want to run any JUnit 4 test we include that junit:4.12 dependency. This just ensure the correct flavor of JUnit is used for the testing. Now we’ll add one more line to our build.gradle to make sure we enable Gradle’s native JUnit 5 support. Add the following lines after the dependencies: 33 test { 34 useJUnitPlatform() 35 } Now hop back into your IDE and work on your project. Save all of your changes and navigate back to your project folder. Open up the src folder. You will see the following sub-folders: main test Both of these contain a folder called java. You will move your JAVA files into the java folders in the main and test subfolders. EXAMPLE: Let’s say I am working a project that has Duck.java , Pond.java , and DuckTest.java. Both Duck.java and Pond.java should be moved to ../src/main/java and DuckTest.java would be moved to ../src/test/java Once you’ve moved your files into the correct location run this in your bash shell: gradle build Once this succeeds run this in your bash shell: gradle test Once this finishes and you get a success message navigate to your project folder and go to /build/reports/tests/test/ and open up the index.html. This will give you a breakdown of how your gradle test went. Now that you’ve sucessfully setup Gradle you need to go back into your IDE and clean up your projects paths so that you are working in the src/main/java folder and the src/test/java folder. See easey peasey lemon squeezy! Next week we’ll go over how to intigrate our projects with GitLab so that GitLab does the testing for us. In the meantime checkout the docs up on Gradle.org related to testing with JAVA & JVM: https://docs.gradle.org/5.2.1/userguide/java_testing.html#using_junit5 #CS@Worcester #CS443

From the blog Michael Duquette by Michael Duquette and used with permission of the author. All other rights reserved by the author.

Patterns Part Deux – A Rusty Duck

Last week we discussed some of the design patterns we’ve been studying. We shuffled those poor ducks around a lot! We did finally land on the Singleton Pattern. The Singleton restricts the instantiation of a class to one “single” instance. Also providing a single point of access to the instance. Wait, is that our new type of duck, The Singleton Duck? How would that work? Is Singleton Duck like Leisure Suit Duck? Just more aloof and dapper? Ok let’s not get carried away with the ducks. The Singleton design pattern is part of the Creational group of patterns. Design Patterns are typically broken out into three general categories: Creational patterns – these provide object creation mechanisms that promote flexibility and reuse of code. Structural patterns – these patterns show how to assemble objects and classes into larger structures while keeping the structures flexible and efficient. Behavioral patterns – these take care of effective communication and assigning responsibility between objects. What’s similar between these three categories? At their core they all promote flexibility and efficiency. Is this why we use design patterns? Well, to some degree yes. Design patterns can be thought of as tried and true solutions to common problems in software design. Over on [Refacturing.guru](https://refactoring.guru/design-patterns) they show 22 designs patterns that fall into the three categories above. Shall we dig into all 22 of them? How about, instead of me typing each one out with a description, you head on over there and check it out. One of the things I found helpful with the information on Refactoring.guru is the description and breakdown of each of the patterns. Here’s a high-level what each pattern contains from Refactoring.guru. Each pattern starts off with a description (the Intent) and has the following sections: Intent Problem Solution Real-world analogy Pseudo-code Applicability How to implement Pros and Cons Relations with other patterns The real-world analogy helped to solidify the concepts and I found the pseudo-code helpful. Sticking with Singleton pattern here is the How to Implement section from Refactoring.guru: How to Implement 1. Add a private static field to the class for storing the singleton instance. 2. Declare a public static creation method for getting the singleton instance. 3. Implement “lazy initialization” inside the static method. It should create a new object on its first call and put it into the static field. The method should always return that instance on all subsequent calls. 4. Make the constructor of the class private. The static method of the class will still be able to call the constructor, but not the other objects. 5. Go over the client code and replace all direct calls to the singleton’s constructor with calls to its static creation method. Pretty straight forward right? Knowing that these design patterns exist, not necessarily memorizing them, and understanding their benefits is a handy tool to have in your box of tricks. Which brings up next weeks topic: Utility Belt – Accessory or Fashion Necessity? Don’t forget to check out [Refacturing.guru](https://refactoring.guru/design-patterns) ! #CS@Worcester #CS343

From the blog Home | Michael Duquette by Michael Duquette and used with permission of the author. All other rights reserved by the author.

Patterns Part Deux – A Rusty Duck

Last week we discussed some of the design patterns we’ve been studying. We shuffled those poor ducks around a lot! We did finally land on the Singleton Pattern. The Singleton restricts the instantiation of a class to one “single” instance. Also providing a single point of access to the instance. Wait, is that our new type of duck, The Singleton Duck? How would that work? Is Singleton Duck like Leisure Suit Duck? Just more aloof and dapper? Ok let’s not get carried away with the ducks. The Singleton design pattern is part of the Creational group of patterns. Design Patterns are typically broken out into three general categories: Creational patterns – these provide object creation mechanisms that promote flexibility and reuse of code. Structural patterns – these patterns show how to assemble objects and classes into larger structures while keeping the structures flexible and efficient. Behavioral patterns – these take care of effective communication and assigning responsibility between objects. What’s similar between these three categories? At their core they all promote flexibility and efficiency. Is this why we use design patterns? Well, to some degree yes. Design patterns can be thought of as tried and true solutions to common problems in software design. Over on [Refacturing.guru](https://refactoring.guru/design-patterns) they show 22 designs patterns that fall into the three categories above. Shall we dig into all 22 of them? How about, instead of me typing each one out with a description, you head on over there and check it out. One of the things I found helpful with the information on Refactoring.guru is the description and breakdown of each of the patterns. Here’s a high-level what each pattern contains from Refactoring.guru. Each pattern starts off with a description (the Intent) and has the following sections: Intent Problem Solution Real-world analogy Pseudo-code Applicability How to implement Pros and Cons Relations with other patterns The real-world analogy helped to solidify the concepts and I found the pseudo-code helpful. Sticking with Singleton pattern here is the How to Implement section from Refactoring.guru: How to Implement 1. Add a private static field to the class for storing the singleton instance. 2. Declare a public static creation method for getting the singleton instance. 3. Implement “lazy initialization” inside the static method. It should create a new object on its first call and put it into the static field. The method should always return that instance on all subsequent calls. 4. Make the constructor of the class private. The static method of the class will still be able to call the constructor, but not the other objects. 5. Go over the client code and replace all direct calls to the singleton’s constructor with calls to its static creation method. Pretty straight forward right? Knowing that these design patterns exist, not necessarily memorizing them, and understanding their benefits is a handy tool to have in your box of tricks. Which brings up next weeks topic: Utility Belt – Accessory or Fashion Necessity? Don’t forget to check out [Refacturing.guru](https://refactoring.guru/design-patterns) ! #CS@Worcester #CS343

From the blog Michael Duquette by Michael Duquette and used with permission of the author. All other rights reserved by the author.

REST not relaxing

Part of working with the LibreFoodPantry includes working with the FoodKeeper-API. So let’s talk about API’s, specifically REST API’s. REST is an acronym that means REpresentational State Transfer. REST is a resource based, not action based, architectural style that has six design constraints. REST architectural style, data and functionality are considered resources and are accessed using Uniform Resource Identifiers (URIs). The clients and servers exchange representations of resources by using a standardized interface and protocol – typically HTTP. BUT… REST is not HTTP! In order to be considered a RESTful API the design must meet the first 5 of these 6 design constraints: Client–server – The interface separates the client from the server. Cacheable – Clients can cache responses. Responses have to, implicitly or explicitly, define themselves as cacheable, or not. This prevents clients from reusing stale or inappropriate data in response to further requests. Uniform interface – REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and hypermedia as the engine of application state. See this article on DZone for a better understanding of HATEOAS: https://dzone.com/articles/hypermedia-driven-rest-services-with-spring-hateoa Stateless – The necessary state to handle the request is contained within the request itself. Layered system – The layered system style allows an architecture to be composed of hierarchical layers by constraining component behavior such that each component cannot “see” beyond the immediate layer with which they are interacting. Code on demand (this is the only optional constraint) – REST allows client functionality to be extended by downloading and executing code in the form of applets or scripts. This simplifies clients by reducing the number of features required to be pre-implemented. Generally, the four primary HTTP verbs used are as follows: GET Read a specific resource (by an identifier) or a collection of resources. PUT Update a specific resource (by an identifier) or a collection of resources. Can also be used to create a specific resource if the resource identifier is known before-hand. DELETE Remove/delete a specific resource by an identifier. POST Create a new resource. Also a catch-all verb for operations that don’t fit into the other categories. A little digging on the web and you will find a lot if resources concerning REST. One very helpful sight I found for getting a better understanding is https://www.restapitutorial.com When you dig deeper into and need to use a client I recommend Insomnia (https://insomnia.rest/) #CS@Worcester #CS448

From the blog Home | Michael Duquette by Michael Duquette and used with permission of the author. All other rights reserved by the author.

REST not relaxing

Part of working with the LibreFoodPantry includes working with the FoodKeeper-API. So let’s talk about API’s, specifically REST API’s. REST is an acronym that means REpresentational State Transfer. REST is a resource based, not action based, architectural style that has six design constraints. REST architectural style, data and functionality are considered resources and are accessed using Uniform Resource Identifiers (URIs). The clients and servers exchange representations of resources by using a standardized interface and protocol – typically HTTP. BUT… REST is not HTTP! In order to be considered a RESTful API the design must meet the first 5 of these 6 design constraints: Client–server – The interface separates the client from the server. Cacheable – Clients can cache responses. Responses have to, implicitly or explicitly, define themselves as cacheable, or not. This prevents clients from reusing stale or inappropriate data in response to further requests. Uniform interface – REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and hypermedia as the engine of application state. See this article on DZone for a better understanding of HATEOAS: https://dzone.com/articles/hypermedia-driven-rest-services-with-spring-hateoa Stateless – The necessary state to handle the request is contained within the request itself. Layered system – The layered system style allows an architecture to be composed of hierarchical layers by constraining component behavior such that each component cannot “see” beyond the immediate layer with which they are interacting. Code on demand (this is the only optional constraint) – REST allows client functionality to be extended by downloading and executing code in the form of applets or scripts. This simplifies clients by reducing the number of features required to be pre-implemented. Generally, the four primary HTTP verbs used are as follows: GET Read a specific resource (by an identifier) or a collection of resources. PUT Update a specific resource (by an identifier) or a collection of resources. Can also be used to create a specific resource if the resource identifier is known before-hand. DELETE Remove/delete a specific resource by an identifier. POST Create a new resource. Also a catch-all verb for operations that don’t fit into the other categories. A little digging on the web and you will find a lot if resources concerning REST. One very helpful sight I found for getting a better understanding is https://www.restapitutorial.com When you dig deeper into and need to use a client I recommend Insomnia (https://insomnia.rest/) #CS@Worcester #CS448

From the blog Michael Duquette by Michael Duquette and used with permission of the author. All other rights reserved by the author.

Quackers about Patterns

So far we’ve dealt with design smells and played with the Duck factory. Which is a great segue into our next topic, Design Patterns, one of which is the Factory Pattern. Design patterns allow us to look at our code, usually as the need for changes arise and utlize the benefits of a design pattern. With our Duck Factory exercises this is where we began. We created duck objects and added them to a pond. As the number of types of ducks changed so did their behaviors. Does a rubber duck quack? Can a cement duck float? Does decoy duck fly? Our initial Factory design pattern worked fine until we our duck types changed and it became evident that the design pattern needed to evolve. Out first iteration took the factory design pattern and migrated to an Abstract Factory pattern. Now our ducks could inherit behaviors from interfaces. But should a decoy duck be inheriting quack behaviors? The Abstract Factory design pattern is really just a transitional pattern to the Strategy Pattern. As we moved our code around it became clear that we were making things a little more complicated than they needed to be. We were starting to introduce some needless design smells. The advantage of the Strategy Pattern is it favors encapsulation. The “Strategy” is to define a set of algorithms, encapsulate each one and make them interchangeable. This allows the algorithms to vary independently from the clients that use them. In the case of our Duck Factory and the ducks (and duck sub-classes) use the encapsulated behaviors (and sub-behaviors) to fly, quack, squeak, and in the case of cement duck sink. We covered 3 other design patterns and next week I’ll touch on those. Until then happy coding! #CS@Worcester #CS343

From the blog Home | Michael Duquette by Michael Duquette and used with permission of the author. All other rights reserved by the author.