<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vikram Chatterjee</title>
    <description>The latest articles on DEV Community by Vikram Chatterjee (@vchatterjee).</description>
    <link>https://dev.to/vchatterjee</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F511090%2Ffc26c67b-8873-4d1d-b5ae-7844247201ca.png</url>
      <title>DEV Community: Vikram Chatterjee</title>
      <link>https://dev.to/vchatterjee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vchatterjee"/>
    <language>en</language>
    <item>
      <title>Why we use setState(): Avoid mutating State in React</title>
      <dc:creator>Vikram Chatterjee</dc:creator>
      <pubDate>Mon, 19 Apr 2021 23:26:59 +0000</pubDate>
      <link>https://dev.to/vchatterjee/why-we-use-setstate-avoid-mutating-state-in-react-1aj1</link>
      <guid>https://dev.to/vchatterjee/why-we-use-setstate-avoid-mutating-state-in-react-1aj1</guid>
      <description>&lt;p&gt;In my Phase Five project review, I was discussing a controlled form that I had created which sent data to an action creator in order to create a new workout session. In the handleChange function, I used a function called setState, and passed in an object with a key of [event.target.name] and set its value to event.target.value. My reviewer asked me what would happen if I were to, instead of using setState, set this.state directly by assigning this.state to the new state?  &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iU51PYiT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qdguo4rmwqauib7u0zds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iU51PYiT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qdguo4rmwqauib7u0zds.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
(Figure 1)&lt;/p&gt;

&lt;p&gt;As you can see, in the above screenshot in the handleChange function, we are attempting to change the state of [event.target.name] to event.target.value. The problem is, when we actually type in our input box, the new state of the form is not rendered until we refresh the page. This is the major issue that we come across when we try to mutate state, or reassign its value without using the setState() function. &lt;/p&gt;

&lt;p&gt;The setState() function, when called, marks a react component as in need of a rerender. However, when we reassign state using 'this.state=', we are not re-rendering the component, so changes to the state will not show up in our controlled from. Furthermore, any state mutations that we make using 'this.state='   could be overwritten when we make subsequent changes  using setState. Thus, it is better to never modify state directly, and instead to always use the setState function, as shown below, in figure 2, which will re-render the component on change and not lead to accidentally overwriting changes in state.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E_z7b2VO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d4cu1fvv7r6bnifgo1bl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E_z7b2VO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d4cu1fvv7r6bnifgo1bl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
(Figure 2)&lt;/p&gt;

&lt;p&gt;It is important to note that when we use setState() it is an asynchronous function. This means that if we try and access 'this.state' in the same function as the one in which setState() was called, we may get a result of the state before it was changed. &lt;/p&gt;

&lt;p&gt;Another point which my project reviewer asked me to explain is why we use a spread operator to represent the rest of the state when we are processing an action in the reducer. The reason is that we are only changing the value represented by one of the keys in the store's state. In the figure below, we are using the spread operator to preserve the values of the other keys in the store's state. For example, when we dispatch an object of type 'ADD_SESSION' to the 'sessionsReducer', we return an object starting with '{...state', which here represents the rest of the state that does not have to do with the sessions key. So the existing inspirations, and personal bests, which are represented as values of other keys in the stores state (namely Bests and Inspirations), are preserved when we return the result of 'ADD_SESSION', which is shown below.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HcTiN6N4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gna7r2pc1z5czepyfgz1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HcTiN6N4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gna7r2pc1z5czepyfgz1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Figure 3)&lt;/p&gt;

&lt;p&gt;Overall I enjoyed doing the React Project, and I found Flatiron School to be a really rewarding experience. In short, I made huge strides in my web design ability in five short months and I would highly recommend that ambitious individuals with a knack for problem solving try the course.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Phase Five Project - TrainingBuddy (Vikram Chatterjee, Flatiron - Software Engineering)</title>
      <dc:creator>Vikram Chatterjee</dc:creator>
      <pubDate>Thu, 15 Apr 2021 03:47:56 +0000</pubDate>
      <link>https://dev.to/vchatterjee/phase-five-project-trainingbuddy-vikram-chatterjee-flatiron-software-engineering-n1o</link>
      <guid>https://dev.to/vchatterjee/phase-five-project-trainingbuddy-vikram-chatterjee-flatiron-software-engineering-n1o</guid>
      <description>&lt;p&gt;TrainingBuddy is a React/Redux application with a RAILS API backend that allows a user to keep track of workout sessions and each workout that is a part of that session, their personal bests, and people who inspire them to keep training. The application uses "Thunk middleware" so that asynchronous requests can be made between the Redux store and the backend API. Users can create, view and delete data from the backend using forms, links and buttons that dispatch actions to the store. The application was styled using the "MaterialUI library", in order to make the application more aesthetically pleasing than a regular HTML application. &lt;/p&gt;

&lt;p&gt;Rails Backend&lt;br&gt;
A "Rails API" backend was created for this project using 'rails new phase_five_backend --api --no-test-framework'. The schema consists of sessions; which have a name and a date, and workouts; which have a name, a number of sets, a number of reps, an integer for weight, and a session_id attribute which is used for associating the workout with a specific session. The schema also has inspirations which contains string attributes for name and image and a text attribute for bio, and bests which represents personal bests, which has an attribute of name, weight, and reps. In "routes.rb" there are resources for inspirations, bests, sessions and workouts and the workouts resource is nested within the sessions resource so that the route looks like /sessions/sessionid/workouts/workoutid which corresponds with the 'has_many/belongs_to' relationship between sessions and workouts. Serializers were set up for Sessions and Workouts so that when the API is accessed, the 'has_many/belongs_to' relationship between sessions and workouts is plainly rendered on the sessions route. &lt;/p&gt;

&lt;p&gt;Setting up the React App with Dependencies&lt;br&gt;
In order to set up our react app, we 'cd' into the 'phase_five_project' folder in the terminal and run the command 'create-react-app phase_five_frontend' which generates the initial scaffolding for the project with files such as 'Index.js' and 'App.js', which are the first and second highest level components of our application, respectively. Then we need to add dependencies using the command 'yarn add redux react-redux redux-thunk redux-devtools-extension' and 'yarn add react-router-dom'.  &lt;/p&gt;

&lt;p&gt;Creating the Store&lt;br&gt;
The Redux store was created in "Index.js", the top level parent of the components in the application. Imports like 'Provider', 'createStore', 'applyMiddleware', 'compose', and 'thunk' allow for a Redux store that can communicate with the backend using asynchronous fetch requests. A variable, 'composeEnhancers', is instantiated just below the imports. This variable allows us to use the 'Redux Devtools' extension that we installed above. Another variable, 'store' is instantiated below, which calls the 'createStore' function which takes in the 'rootReducer' and the 'composeEnhancers' variable as arguments. 'composeEnhancers' takes 'applyMiddleware' as an argument, which takes in 'thunk' as an argument in turn. Then the store is passed in the Provider component so that child components of index.js can access the Redux store. Underneath the Provider component, the Router component is rendered so that any child components can access the Router which will allow for client-side routing of the different pages in the application. The parent component of the application, App, is imported and rendered beneath Router and will provide our application with all of the necessary components, as shown in figure 1. &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5in3W-VK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6o8ucw2l6xbr1bkp8tei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5in3W-VK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6o8ucw2l6xbr1bkp8tei.png" alt="figure 1"&gt;&lt;/a&gt;. Figure 1 - index.js&lt;/p&gt;

&lt;p&gt;React Containers and Routes&lt;br&gt;
Inside of 'App.js', we import all of our container components which in turn, contain all of our routes so that a user can see RESTful urls which correspond to each of the Component based views that are contained in the application. Nested within the App div, the Nav component is rendered, which renders a Nav bar at the top of the application page and will be visible regardless of which route the user is viewing. The Nav bar contains links for most of the pages in the application and allows the user to quickly navigate to the desired route depending on which records they would like to view or create. At the root level of the application, the Home component is rendered with a route. The SessionsContainer, BestsContainer, and InspirationsContainer are rendered underneath the switch statement containing the root path, and these containers each contain respective routes that the user can navigate to view or create records. The SessionsContainer is an example for our different containers, and is a class component that uses mapStateToProps and imports an action creator, fetchSessions, which uses an asynchronous fetch request to get our session data from the backend, as shown in Figure 2.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C5ueECbo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmddexeqoprqfc7kunc4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C5ueECbo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmddexeqoprqfc7kunc4.png" alt="figure 2"&gt;&lt;/a&gt;&lt;br&gt;
Figure 2 - fetchSessions&lt;/p&gt;

&lt;p&gt;The SessionsContainer component itself is shown in Figure 3. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ItPwkf2B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bym87k1xidxz3lv1mour.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ItPwkf2B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bym87k1xidxz3lv1mour.png" alt="figure 3"&gt;&lt;/a&gt;&lt;br&gt;
Figure 3 - SessionsContainer&lt;/p&gt;

&lt;p&gt;When the component mounts, the 'fetchSessions' action is called and brings all of the session data from the backend into the Redux store with the help of the reducer. After all of the session data is fetched, the reducer sets the state of the sessions object within the store to the payload of the 'fetchSessions' action, which contains our session objects. Inside of the Switch statement in the 'SessionsContainer', there are three Routes that route the user to the new Session form, the show page of a session, and the index page listing the sessions respectively. It is important that /sessions/new is rendered before /sessions/:id because /:id is a dynamic route which would accept 'new' as a parameter of the URL, so if /sessions/id were rendered first, the router would attempt to navigate to a session with an id of 'new' if someone attempted to navigate to the 'new' route. In the 'new' route, no props need to be passed down from the sessions container to the new form, so we render that route with the 'component' keyword and pass the 'SessionInput' component in using JSX tags. In the /sessions/:id route, we are passing props down from the "SessionsContainer". First we pass in the 'routerProps', which include 'match' which will allow us to get the id of the specific session that we are navigating to. We also pass down the sessions prop which is accessed in the container using 'mapStateToProps'. This contains an array of objects containing data for each session.&lt;/p&gt;

&lt;p&gt;Viewing Records&lt;br&gt;
Earlier in the blog post, it was discussed that in the show and index pages of Sessions, we are passing down the routerProps and the sessions prop which contains data for each of our sessions. In the Sessions (index) component, props are passed in and in the return portion of the component, the array of sessions is mapped over using 'props.sessions.map' and a link to each session's route is rendered with the text being equal to the name of the session. A delete button is rendered next to that link, which, when clicked, will dispatch the 'deleteSession' action which will make a fetch request to update the backend, and then be passed to the reducer to remove that session object from the redux store in the frontend. &lt;br&gt;
The Session component is responsible for showing an individual session to the user. In order to access a specific session object, the filter function is called on 'props.sessions' and filters through the array of session objects to find the session id which matches the id param in 'props.match.params.id' which is passed down from routerProps when a specific Id is passed into the URL as shown in Figure 4.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--22kL7Y40--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11a6dt8aczz4exrvf2i6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--22kL7Y40--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11a6dt8aczz4exrvf2i6.png" alt="figure 4"&gt;&lt;/a&gt;&lt;br&gt;
Figure 4 - Session.js&lt;/p&gt;

&lt;p&gt;The filter method contains an array of our objects which match the 'params', so we must place a [0] after the filter function in order to access the first (and only) session object which matches the filter query. The result of the filter function is set to the variable 'session' and if a session is found, the session name and date will be rendered within 'h2' tags. Below that, we render the 'WorkoutsContainer', which contains a form to enter a new workout as well as a list of workouts that are associated with that session. The session prop is passed into the 'WorkoutsContainer' so that the workouts container only submits and renders workouts that are associated with a given session. The 'WorkoutsContainer' contains two components, 'WorkoutInput' and 'Workouts', which display the form to input a new workout, and render the workouts associated with a session, respectively. 'WorkoutInput' takes props of a session in order to connect the workout with a given session ID, and 'Workouts' takes in the props of a session and the props of a workout associated with the session so that it can iterate over and display each workout associated with that session. &lt;/p&gt;

&lt;p&gt;Adding records to the store and backend&lt;br&gt;
This application uses controlled forms and calls on action creators in order to update the backend and the Redux store. In the 'sessionInput' component, the state is an object containing two key value pairs, 'name' and 'date'. Both are initially set to empty strings. In the render function, a return value is set to a form with 'TextFields' for the session name and date. The values are set to 'this.state.name' and 'this.state.date' respectively. An 'onChange' parameter invokes the 'handleChange' function which uses 'setState' to modify the internal state of the component. The form has an 'onSubmit' parameter which calls on the 'handleSubmit' function. The 'handleSubmit' function prevents the default refresh which would happen when a submit button is clicked, and then calls on the action creator 'addSession' which takes in the component's state and makes a fetch request to the backend and then dispatches an action of type ADD_SESSION to the reducer which contains a payload of the session object to be added to the Redux store's state as shown in Figure 5. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1J7J-wOG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gpbzwrxk5u7fzetg5pm1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1J7J-wOG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gpbzwrxk5u7fzetg5pm1.png" alt="figure 5"&gt;&lt;/a&gt; Figure 5 - sessionsReducer.js&lt;/p&gt;

&lt;p&gt;Another example of a controlled form within this project resides within the 'WorkoutInput' component, which uses 'handleChange' and 'handleSubmit' in a similar manner to the 'SessionInput' component, but contains a state and form which contain the name, sets, reps, and weight of a workout. It is important to note that we have to import the action creators associated with these components, and 'export default connect' the components at the bottom, passing in the action creator as the second argument in the connect function. One difference between the 'addSession' action creator and the 'addWorkout' action creator is that 'addWorkout' takes two arguments: the internal state of the component and the id of the session associated with the workout. The 'addWorkout' function uses the 'sessionId' argument as part of the url of the fetch request, so that the program knows where to go to add the workout to a given session. The action that is dispatched to the reducer actually contains a payload of that workout's associated session, so that the store is updated with the version of the session containing a new workout. &lt;/p&gt;

&lt;p&gt;Deleting Records&lt;br&gt;
For each of the components that render data from our models, there is a delete button beside the name of each object that is rendered. For example, in the Workouts component, we have a button which, when clicked, triggers the 'handleDelete' function with the workout object associated with that list item being passed in as an argument to 'handleDelete'. We import the action creator 'deleteWorkout', and we pass it into the second argument of the connect function at the bottom, which makes the function available as a prop in the component. Inside the 'handleDelete' function, we call 'props.deleteWorkout' and pass in the id of the workout and the id of its associated session so that we can target the workout and modify the associated session in the Redux store. In the fetch request, we interpolate the session id and the workout id in the URL so that we are deleting data from the correct route. When the delete request is finished, we take the session object and dispatch an action to the reducer which maps over the sessions in the store's state and returns the new session which excludes the workout which was just deleted. In the sessions component, we have a similar 'handleDelete' function which in this case only takes the 'sessionId', makes the delete request, and passes the session object into the payload of the dispatch of type DELETE_SESSION, but this time, in the reducer, we use a filter function to return all of the sessions whose id does not match the session passed into the payload of the dispatch object, which modifies the state to exclude the session that was deleted. &lt;/p&gt;

&lt;p&gt;In this blog post, examples from the sessions and workouts components have been given, but the 'inspirations' and 'bests' components work in very similar ways. A summary of the Rails backend, an explanation of store creation, routes and containers, and adding and removing data from the Redux store and backend have been given. To view the project files, go to &lt;a href="https://github.com/Strycora/phase_five_frontend"&gt;https://github.com/Strycora/phase_five_frontend&lt;/a&gt; to view the frontend files, and &lt;a href="https://github.com/Strycora/phase_five_backend"&gt;https://github.com/Strycora/phase_five_backend&lt;/a&gt; to view backend files. &lt;/p&gt;

&lt;p&gt;TrainingBuddy is a great resource for active people who want to log their sessions, workouts, and personal bests! As a bonus, users are able to document those people who inspire them to pursue fitness. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Phase 4 project - Favorite Places (Vikram Chatterjee, March 14th 2021, Flatiron - Software Engineering)</title>
      <dc:creator>Vikram Chatterjee</dc:creator>
      <pubDate>Sun, 14 Mar 2021 21:47:42 +0000</pubDate>
      <link>https://dev.to/vchatterjee/phase-4-project-favorite-places-vikram-chatterjee-march-14th-2021-flatiron-software-engineering-42p7</link>
      <guid>https://dev.to/vchatterjee/phase-4-project-favorite-places-vikram-chatterjee-march-14th-2021-flatiron-software-engineering-42p7</guid>
      <description>&lt;p&gt;For my Phase 4 project, I wrote a simple SPA (single page application) that allows users to document their favorite places. Users are able to click on the "Add a Place" and enter the name of the place, a description, and an image URL which will cause the image to display on the list of places below the description. Users are able to add comments to places, which will be displayed in the 'div' (divider) of a place below its description or below the image if one was created. &lt;/p&gt;

&lt;p&gt;The Github repository is divided into two major components, the frontend and the backend. The backend is a Rails API, which I generated using the rails command 'rails new phase_four_project --api'. The models in the backend represent Places and Comments, where a Place has many Comments and each comment belongs to a place. The models for Places and Comments were generated using the command 'rails g scaffold' which creates ActiveRecord models, controllers, migrations and routes in the backend. When using 'rails g scaffold' in API mode, the controllers are automatically populated with CRUD actions that render json objects that represent the instance or instances of the objects that get accessed by the frontend or the Rails route of the backend (for example, &lt;a href="http://localhost:3000/places"&gt;http://localhost:3000/places&lt;/a&gt;). In order to run the application, the user must 'cd' into the backend and run 'rails s' so that the fetch requests in the frontend have an active server to communicate with. &lt;/p&gt;

&lt;p&gt;The frontend of the application is rendered using the 'index.html' file, which employs several javascript files that provide access to the models of Place and Comment (via comment.js and place.js), global variables that use Javascript to access HTML nodes (via globals.js), API fetch requests (via api.js), and miscellaneous functions (via index.js). The functionality of the javascript files is divided according to separation of concerns, separating models from services and other functions, which makes it easier for developers to find what they're looking for. &lt;/p&gt;

&lt;p&gt;Globals.js&lt;br&gt;
'globals.js' is a javascript file in the root directory of the Javascripts folder which contains arrow functions that get elements in the DOM by their ids. These functions can be chained onto in order to add an event listener or some other functionality to elements in the DOM. For example, the function formLink() is used by the formLinkEvent() function in index.js to add an event listener to the "Add a Place" link so that when the link is clicked, the DOM will render the form to instantiate a new place. &lt;/p&gt;

&lt;p&gt;Place.js&lt;br&gt;
The functions for the model that represents 'Places', the primary model of the application, are stored in place.js. This javascript file contains a constructor for instances of Places, which sets the attributes of id, name, description, and imageUrl in order to create a javascript object that corresponds with the ActiveRecord object in the backend. A static function called 'all' contains an empty array into which these javascript objects are added. The static function 'createFromCollection' takes a list of places formatted as JSON data and creates (instantiates and saves) javascript objects with the given attributes, saving them into the 'all' array. The 'place.js' file includes two templates, placesTemplate and formTemplate, which add HTML to the DOM. placesTemplate creates an h3 containing the string "My Favorite Places" and a div with an id of places in which each div listing a place will be stored. The formTemplate adds an HTML form to the DOM which contains fields for the name of the place, a description, and an imageUrl, as well as a submit button. The renderForm function clears out the DOM  and adds the placesTemplate and the formTemplate to the DOM, and adds an event listener to the submit button which runs the submitForm function when clicked. The submitForm function, like many functions in this app, runs a function called 'e.preventDefault' which prevents the page from redirecting. The submit form then creates a variable, strongParams, which contains the values of the name, description, and imageUrl of the instance of a place which is being created, and then uses the Api.post method to create a Place, assigning the attributes in the strongParams variable to attributes of a new Javascript 'place' object, and saves it into the 'all' array, and then calls the renderPlaces function to iterate over each place and render it using renderPlace, and then get the comments for each place. The renderPlace function creates a div to contain the attributes of the place, and creates an h4 for the title, a p for the description, and an img to render whatever image was submitted in the imageUrl section of the form. It also creates a delete link and a comment button, and adds event listeners that run functions that create the comment or delete the place. At the end of the renderPlace function, the elements containing the place name, place description, and image, as well as the delete link and the comment button, are appended to the div (an image is only appended if the imageUrl is populated with a string). Then, the div containing the attributes of the place is appended to the div containing the list of places. The static function getPlaces gets the JSON data from the '/places' route and creates all of the places using createFromCollection, and then renders the list of places. The deletePlace function is attached to the event listener for the deleteLink in renderPlace. A fetch request to delete the place is called using Api.delete where '/places/' + id is filled into the arguments, which deletes the place JSON object from the Api. The function then iterates over each place until a place with a matching name is found, and then mutates the Place.all array using '.splice' to remove one item at the index where the place with the matching name was found. &lt;/p&gt;

&lt;p&gt;Comment.js&lt;br&gt;
The comment.js file contains all of the function pertaining to comments, and starts with a constructor that creates a javascript object, taking in the attributes of id, text, and place_id. The static function all contains an empty array into which these comments are populated. The static asynchronous function getComments clears all of the comments from the all array and iterates over each JSON object in the comments index route, and instantiates a new comment using the constructor to assign attributes, and then renders each comment using the function renderComment. The renderComment function gets the div of the place that the comment belongs to and adds a p tag to the div, appending it to the div and setting the text of the p tag to the text of the comment. The getComments function is used to render all of the comments for the places when the 'listofplaces' is requested, and the addComment function is used when a comment is being added to a place. The addComment function is tied to an event listener on the commentButton which was created in the renderPlace function. In the addComment function, an input is assigned to the variable commentInput, and it is assigned a type of Text and a name, id, and placeholder. This is where the comment text can be typed in. A variable commentSubmit is assigned to a button, whose id is assigned as commentSubmit and whose innerHTML is assigned as "Add Comment". If the commentInput has not already been added to the DOM, it will be appended to the div of the place that the comment is being added to. An event listener is added to the commentSubmit button, which prevents the default redirect and assigns a variable of text to the value typed into commentInput, and a variable called 'comment' is assigned the value of an object that contains the text of the comment and the id of the place that the comment is being added to. Then a post request is made using the Api.post function, with '/comments' and the 'comment' variable being passed into its arguments. Then an arrow function taking in the variable comment is called, which creates a variable called 'c' and assigns it to a new Comment which is instantiated using the constructor. Then the text stored in the javascript object 'c' is assigned to a new p tag which is appended to the div of the place that the comment is being added to. Finally, c.save() is called which adds the comment to the total array of comments.  &lt;/p&gt;

&lt;p&gt;Api.js&lt;br&gt;
The api.js file is a javascript class that contains the baseUrl of the rails Api where all of the fetch requests are coming from, and handles all of the fetch requests. The static baseUrl is set to 'localhost:3000', where the rails server is running. The static headers contains an object that has two parameters, "Accept" and "Content-Type", which are set to "application/json" so that the fetch requests know that they are taking in JSON data. The static async function 'get' takes in a path that is appended to the baseUrl (either '/places' or '/comments') in the fetch request, which includes an object containing with the method parameter 'GET' and a parameter set to the 'Api.headers' static function. The variable data is assigned to the response of the fetch request, which is parsed into JSON, and then the value of that 'data' variable is returned to whatever function made the GET request. A similar technique is used in the static async function "post". The only difference between the get and post functions is that the post function takes in params (for example, the strongParams variable in the submitFunction form) and includes a 'body' attribute which is set to JSON.stringify(params) so that the data being submitted to the server is parsed into JSON. A variable 'data' is again assigned to the response from the JSON server, which is then returned to the function making the POST request. The static async function 'delete' works just like the 'get' function, except that the 'method' assigned to the parameters within the fetch request is a 'DELETE' method instead of a 'GET' method. The object that is being deleted is assigned to the 'data' variable and returned to the function that made the delete request.&lt;/p&gt;

&lt;p&gt;Index.js&lt;br&gt;
Whatever functions did not neatly fit into any of the concerns of the models, services, or global element variables are put into the index.js javascript file, which is located in the root directory of the javascripts folder. The index.js file contains four functions, resetMain(), formLinkEvent, placesLinkEvent, and an event listener which is appended to the document. The resetMain function is called when a form or the list of places is being rendered, and resets the innerHTML of the DOM so that new data can be appended. The formLinkEvent function listens for when the formLink (assigned to the "Add a Place" link in index.html) is clicked, and prevents a redirect and calls Place.renderForm, which renders a form for the user to enter a new place into the list. the placesLinkEvent() function adds an event listener to the placesLink() ("List of Places" link in index.html) and prevents a redirect, and then calls Place.renderPlaces to render the list of places that are stored in the API. The document.addEventListener function activates when the content in the DOM is loaded, and adds the placesTemplate (containing the div that contains each place's div), calls getPlaces(which in turn, calls renderPlaces which calls getComments) so that all of the places and comments are listed in the DOM when the page is refreshed. Then, formLinkEvent() and placesLinkEvent are called on, which causes the links to add a place and to view the list of places to work. &lt;/p&gt;

&lt;p&gt;Conclusion:&lt;br&gt;
The 'Favorite Places' app uses a Ruby on Rails backend to render JSON-formatted objects to the Javascript frontend, which contains several classes that are separated into concerns and use that JSON data to allow the user to fill in forms that add places to a list of places, delete places from the list, and add comments to each respective place. It is my hope that users have fun exploring the world and documenting their favorite places using my app, creating comments to share their memories or observations relating to each of their Favorite Places. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Phase 3 project - PushWith</title>
      <dc:creator>Vikram Chatterjee</dc:creator>
      <pubDate>Sun, 07 Feb 2021 20:32:39 +0000</pubDate>
      <link>https://dev.to/vchatterjee/phase-3-project-pushwith-3ecl</link>
      <guid>https://dev.to/vchatterjee/phase-3-project-pushwith-3ecl</guid>
      <description>&lt;p&gt;For my phase 3 project I designed an application called PushWith - The definitive application for gyms that want to help members connect through workout groups and post their workouts online for others to see!&lt;/p&gt;

&lt;p&gt;Routes: The root route to sessions@welcome contains the landing page from which users can go to the signup, login, and Omniauth routes. Get and post routes to signup and login have been created and the get routes have been aliased as #signup and #login so that the users know what they are doing at those pages (signup, for instance, is more intuitive to a user than /users/new). A delete route to 'logout' has been implemented so that users can logout without having to clear their cookies. A match route to the google omniauth route has been created so that users may log in or sign up with Google. A match route to '*path' has been created so that a user entering an invalid route within the domain is safely routed back to the landing page without having to see a spooky Rails error. Resources for workout groups and nested resources for workouts have been created so that workout groups and Workouts have full CRUD functionality and so that routes for workouts are properly embedded within the routes for the workout groups that they belong to. New, Create, and Destroy resources for UserGroups have been created so that instances of a join table between a User and a Workout Group can be created when a User wants to create or join a Workout Group. &lt;/p&gt;

&lt;p&gt;Login, Signup, and Omniauth: When users first navigate to the website, they are able to create accounts by clicking sign up on the landing page and entering a username and password. The user model validates the presence of a username and uses BCrypt's #has-secure-password method to validate the presence of a password. If a user fails to fill out either form, they will be met with a flash error that will tell the user that the username and/or password cannot be blank. When the user creates an account successfully, a session will be created and the user id will be saved, which logs in the user and allows them to interact with the application. When the required fields are filled out and submitted, the User object will be saved to the database, allowing a user to login with their credentials from the landing page at a later time. Before the user is saved into the database, the password is salted and converted to a hash (salted meaning that a short string is added to the end of the password) by the BCrypt gem and saved as a password digest in order to ensure that the raw password never enters the database, in order to ensure maximum security. &lt;/p&gt;

&lt;p&gt;When a user logs into the application, the sessions controller #create method finds the user by username and uses BCrypt to authenticate the user by adding the salt (a short string) to the raw password and uses its algorithm to translate the raw password with salt into the hashed password digest, and compares the hashed value to the password digest value in the database. If the values match, the user will be logged in and redirected to the workout groups index page, where they will be able to see the list of workout groups which have already been created. &lt;/p&gt;

&lt;p&gt;On this platform, users are given the option to login with Google instead of entering a username and password on the signup page. On the landing page there is a link to "Log in with Google", which uses Omniauth to create, save, and access user accounts by entering their Google credentials via a Google secured path. This offers users added security, because even if the application is hacked, there will not even be a password digest on the server for a hacker to try and decrypt. Omniauth was implemented by going into Google's developer console and creating an OAuth 2.0 Client ID, and using the credentials generated to initialize an instance of Omniauth in the omniauth.rb file in /config/initializers. In order to get Omniauth to work, 4 gems were added to the gemfile: 'omniauth', 'dotenv-rails', 'omniauth-google-oauth2', and 'omniauth-rails_csrf_protection'. The provider google_oauth2 was set in the omniauth initializer file, and the Google Client key and Client secret were securely stored in a local .env file which is not pushed to Github, so that the credentials are only accessible to the machine that is running the server. &lt;/p&gt;

&lt;p&gt;When a user logs in with Google, the #google method in the Sessions controller finds or creates a user by making a request to Google using the Client ID and Client Key stored in the .env file and gets the name of the user from their Google profile. Then, a 10 character hexadecimal password is generated in order to generate the session and login the user. This means that a user that signs up with Google must always login using Google, as the SecureRandom password is never accessible to the user or the database. If the user is saved to the database they will be logged in and their username will be their name according to Google. Otherwise, they will be redirected to the landing page or 'root path'.    &lt;/p&gt;

&lt;p&gt;Models, Validations and Associations: The PushWith application has a total of 4 models that inherit from ApplicationRecord: The models are for User, Workout Group, UserGroup, and Workout. The User model has many user_groups, and has many workout_groups through user_groups. The User model validates the presence and uniqueness of Username, and uses the has_secure_password method to validate the presence of a password and to encrypt the password as was discussed in the previous section. &lt;/p&gt;

&lt;p&gt;The WorkoutGroup model has many UserGroups and deletes all UserGroups it is associated with when a given WorkoutGroup is deleted. The WorkoutGroup model has many Users through UserGroups. It also has many Workouts and deletes all associated workouts when an instance of the class is deleted. The WorkoutGroup model validates that its instances have a name. The WorkoutGroup model is special in that it accepts nested attributes for UserGroups and optional nested attributes for Workouts. This comes in useful when building a new WorkoutGroup. As we will discuss in an upcoming section, the New form for a workout group will generate a UserGroup and new Workouts using nested forms. There are two methods in the WorkoutGroup class that utilize the ActiveRecord query method #where. The first of these methods uses a search bar on the WorkoutGroups index page to search for Workout Groups whose name contains the string that is typed out in the search bar. The second method, #find_current_user_group, helps to find the UserGroup that belongs to a given WorkoutGroup and is associated with the user that is currently logged in. &lt;/p&gt;

&lt;p&gt;The UserGroup model serves as a join table between Users and WorkoutGroups, and as such it belongs to a workout_group and belongs to a user. The UserGroup class validates the uniqueness of a workout_group_id within the scope of user_id, which is to say that it prevents a given User from entering a WorkoutGroup that he or she has already joined. The UserGroup class has a string attribute of mantra, which must be present in order for a given user_group to be saved. The mantra of the UserGroup is displayed on the WorkoutGroup's show page next to the username of the user that the UserGroup is associated with. &lt;/p&gt;

&lt;p&gt;The Workout model is a simple model whose instances belong to a WorkoutGroup. The Workout validates the presence of all of its attributes: a string of 'name', and integer values for 'sets' and 'reps'. &lt;br&gt;
  The above models make for an application in which each user can join many workout groups, and each workout group may contain many users. Each time a user enters a workout group, they must submit their mantra to join, and they may have a different mantra for each workout group that they join. When a workout group is created or at any time while the workout group exists, workouts can be created for it and are listed in a nested route that displays the workouts for a given workout group. &lt;/p&gt;

&lt;p&gt;Controllers: As with any application, the controllers for the PushWith application are essential to navigating through the application, and in particular for Creating, Reading, Updating, and Deleting objects contained within the application. All controllers in the application inherit from ApplicationController and may use any of the methods within it. &lt;/p&gt;

&lt;p&gt;The ApplicationController contains 5 methods, and two of these methods are helper methods, #user_signed_in? and #current_user,  that may be accessed by views in addition to being accessible by the other controllers. The #user_signed_in? method is used to confirm that a session currently exists for a user; it checks to make sure that a user is logged in. The #current_user method actually employs the #user_signed_in? method and goes further to check to see which user is signed in, by finding that user by their user id. The login_user method uses the instance variable @user to get the id of a user that is logging in or signing up, and creates a session for that user in order to log the user in. The #redirect_if_not_logged_in method checks to see if a user is signed in, and if there is no user signed in, it is employed in various cases in the other controllers in order to prevent a user that is not logged in from performing certain actions, and to notify the user that they must sign in in order to perform that action. Finally, there is a fallback method that redirects a user back to the landing page 'root_path' if they attempt to use the URL bar to navigate to a route that does not exist within the application. &lt;/p&gt;

&lt;p&gt;The first controller that inherits from the ApplicationController is the SessionsController, which houses the landing page 'root_path' at sessions#welcome. The sessions controller takes care of logging in users; besides the landing page, the sessions controller only handles one view in the application: the login page. The #new action in SessionsController routes a user to this page, which asks for the username and password of an existing user. The #create action in SessionsController handles post requests that are made in this view, and creates an instance variable @user that is set equal to the instance of an existing user whose username matches the username that is typed in the 'username' field. As was discussed in a previous section, the #create method only proceeds if a user with a matching username is found. Upon finding a matching username, the #create method uses BCrypt to authenticate the user in the manner that was previously discussed. If a user is authenticated, they will be logged in and redirected to the workout_groups_path. Otherwise, the flash error "Invalid Username or Password" will show and the new form will be re-rendered to the user. The #destroy action in SessionsController is attached to the "Log Out" button in the nav bar, and will clear the session that saved the logged in status of a given user, and it will redirect that user to the root path. The #google method allows Omniauth to find or create a user by assigning the username attribute to the 'name' extracted from google, and creates a SecureRandom 10-digit hex password for authentication purposes. The #google method employs the private method, #auth, which goes into the .env file and uses the Google Client ID and Secret Key to extract information from Google, as was discussed in a previous section.&lt;/p&gt;

&lt;p&gt;While the SessionsController takes care of logging in users and Omniauth actions, the UsersController takes care of creating new users. The #new action in UsersController navigates the user to the /users/new or signup route, and creates the instance variable @user which will be reused in the #create method. The #create method takes the instance variable #user, and takes the argument of user_params provided by a private method, in order to make sure that a username and password, and only a username and password, have been entered  and save it to the database. If valid information is input into @user, the #create method will validate that the User model did save the user and it will use the login_user method to create a session for that user, and then redirect the user to the workout_groups_path. Otherwise, flash errors will pop up saying that the username and/or password cannot be blank, and the new page will be rendered again.&lt;/p&gt;

&lt;p&gt;The UserGroups Controller is a simple controller that is only directly associated with one view, which allows a user to join an already existing workout group. The private method, #user_group_params, permits only the params of mantra, workout_group_id, and user_id to be entered into the params for @user_group. The #new action renders the Join a Workout page and sets the instance variable @user_group to a new UserGroup object. The #create method takes care of post requests to the user_groups_path and employs the user_group_params method to make sure that only valid attributes are entered into the @user_group instance variable. It then checks with the UserGroups model to make sure that all of the required attributes have been submitted, and if the UserGroup is saved into the database, the user will be redirected to the workout_groups_path. Otherwise a flash error will come up telling the user which required attributes have not been entered, and re-render the Join a Workout Group form. The #destroy action in User Groups Controller is linked to the Workout Groups show view, which contains a link to leave a given workout group which only renders if the user is currently in that workout group. The Workout Group controller employs the #find_current_user_group method in the WorkoutGroup model to find the user group that is associated with both the workout group and the current user in order to display the link.  When that link is clicked, the #destroy action and the #find_user_group method in the UserGroups controller, select and destroy the current user's UserGroup, which removes the given user and their mantra from the workout groups roster. &lt;/p&gt;

&lt;p&gt;The Workout Groups Controller handles the New Workout Group route and the Workout Group Show route, where it uses the WorkoutGroup model's find_current_user_group method to help to render the leave workout group link for logged in users that are in a given workout group. It also handles as the Workout Group Index route, and its #create method handles any post requests to the New Workout Group route. Furthermore, the Workout Groups Controller renders the edit page for a Workout Group which allows users in the Workout Group to change the Workout Group's name, handles Patch requests to a workout group, and handles requests to delete a workout group, by finding the workout group by id and destroying all associated user groups and the workout group, and leaving a flash notice that mentions the name of the workout group and saying that it was deleted. The #index action contains a conditional to check for a query entered in the search bar, and uses the WorkoutGroup model's search method to find only the workout groups whose name contains the query entered in the search bar. The #show action uses #find_workout_group to find a given workout group by id, and contains instance variables for @user_groups and @user_group to respectively display the user groups that belong to the workout group and, optionally, the link to leave the workout group if the current user is in that workout group. The #new action instantiates a @workout_group variable and three instances of workouts for the user to optionally add three workouts to a workout group when the workout group is created. The #workout_group_params method is used in the #create action when a post request is made to make sure that only a name, a user group id and mantra, and workouts with their names, sets and reps are entered into the workout group params. If the workout group is saved after the new workout group form is filled, the user will be redirected to the workout groups path. Otherwise, the new form will be re-rendered to the user. The Workout Groups Controller also contains a redirect method that defends the workout group from being edited or deleted by a user that is not in that workout group, as well as a redirect method that prevents the user from navigating to a workout group that does not exist. &lt;/p&gt;

&lt;p&gt;The Workouts Controller handles the #new and #create action for workouts to be created in a workout group. The #new action creates a @workout instance variable and associates the workout with the workout group that it is being created in. The #create action uses #workout params to make sure that only valid attributes of a workout, :name, :sets, :reps, and :workout_group_id are being passed into a new Workout object before it is saved. If a workout is saved, the user will be redirected to the workout_group_workouts_path (the list of workouts in a workout group), otherwise, the new form will be re-rendered to the user. The #show action handles the show page for a workout, which displays its name, sets, and reps. The #edit action renders the edit page, which allows the user to change any of these attributes of a workout. The update action takes care of post requests and verifies that only permissible #workout_params are being entered, like the #create action does. The #index action handles the index page; it checks to see that there is an associated workout group and displays all of the workouts associated with that group. The #destroy action selects and destroys a given workout by ID, and renders a flash notice saying that the workout with the given name was deleted, and redirects them to the new list of workouts. In the Workouts controller, there are redirect methods that prevent the user from navigating to a workout that doesn't exist, a workout that does not have an associated workout group, or a workout that belongs to a group other than the workout group with the associated workout group id. There is also a method that ensures that a user that is not in a given workout group cannot create, edit, or delete workouts in that workout group. &lt;/p&gt;

&lt;p&gt;In the User_Groups controller, the WorkoutGroups Controller and the Workouts Controller, the before_action method was used in order to target instances of objects as well as to facilitate redirections if an invalid path was selected for a given controller. The before_action method calls a method and runs the logic in that method before the action is run. One case in which a redirect method was called in the action instead of using a before is in the Workouts Controller's new method, because a @workout instance variable needed to be generated before the 'redirect_if_not_in_group' method could be run. &lt;/p&gt;

&lt;p&gt;Forms, Nested Forms, Layouts and Partials:&lt;br&gt;
The Application.html.erb file is an important view that renders the materialize CDN as well as javascript methods that allow for methods such as delete to be called in forms. It also instantiates jquery, which must be used to allow materialize initializers to run. The nav partial as well as the errors partial for flash errors are rendered in this view above the body of the website. The errors partial detects and iterates over flash errors, displaying them at the top of the page. The _formerrors partial is called upon in forms like the Workout Group and Workout _form partials, and facilitates the display of errors within the forms that display when invalid data is entered into the forms above the form which is re-rendered to the user. The nav partial calls upon a method in the application helper to render nav links, and renders different links depending on whether the user is signed in or not. &lt;/p&gt;

&lt;p&gt;The UserGroups new form creates a post request to the user_groups_path and contains a hidden field for user_id which is set to the id of the current user. The UserGroups form contains a collection select form that allows the user to access a drop-down menu of all of the workout groups that exist, and select the workout group which they would like to join. At the bottom of the view, there is an initializer that allows Materialize to render this drop-down menu. Below the drop-down menu, a text-field for mantra provides a field for the user to enter their mantra when joining a workout group. The Workout Groups _form partial provides instructions for what to render in the edit and new pages for workout groups. It contains a label and a text field for the name of the workout group. The new page for workout groups renders the layouts/formerrors partial and the _form partial within the form for the workout group. These partials provide errors when they are rendered, and the label and textfield for the workout group's name, respectively. The new form also renders the fields_user partial with locals of f: f and workout_group: @workout_group so that these variables are accessible to the partial. The fields_user partial uses the formbuilder variable, f, to nest itself within the overall form, and the workout_group variable is used to create an associated build between the workout group and the user group. The _fields_workout partial takes locals for 'f' and for 'workout_group' to nest itself within the overall form and to create an associated build between workout_group and workouts. When an associated build is performed, the object that is being created automatically assigns itself to the workout group by adding the value of workout_group_id to the key designated 'workout_group_id' in their respective instances. In this case, since UserGroups and Workouts both belong to Workout Group, their instances contain the foreign key, workout_group_id, which designates which workout group they belong to. The edit form for Workout group contains the _formerrors partial and the _form partial in order to display errors and to render the text field for the name of the workout group. The local 'item:' is assigned to @workout group in the _formerrors partial so that the errors for @workout group are displayed above the form if invalid data is submitted. When the _form partial is rendered, f is passed as a local so that the partial knows about the form builder, and 'workout_group:' is assigned to @workout_group so that the 'name' attribute is assigned to the instance of the WorkoutGroup that is being created. &lt;/p&gt;

&lt;p&gt;The index form for Workout Groups contains a form tag with a GET method that routes to the workout_groups_path so that the query in the text field tag can be used by the Workout Groups controller to filter for workout groups that match the query that is passed into the search bar. Below that, @workout_groups (which the controller designates as being all workout groups, or just the workout groups matching the search query), are iterated over and links are provided that display the name of the workout group and route to the workout group path of that particular workout group. At the bottom of the index page, a link to create a workout group is displayed that routes to the new_workout_group path, which when clicked will display the new workout group form. The show page for a workout group displays the name of the workout group at the top of the page, and below that, it iterates over the user groups that belong to that workout group, displaying the username of the user that owns each user group, and the mantra of that Usergroup afterwards. Below the list of UserGroups, links to the workouts index page, the edit page for the workout group, and the delete route for the workout group are displayed. As was discussed earlier, a link to leave the workout group is displayed if the current user is in the workout group that is being viewed. &lt;/p&gt;

&lt;p&gt;The Workouts' views folder also contains a partial labelled _form that takes the arguments workout_group, and workout. The argument of workout_group is given so that the hidden field in _form for :workout_group_id can be filled with the workout group id of the workout group that the given workout belongs to. The argument of workout is given so that the form knows that the fields within are assigning values for the attributes of the @workout which is initialized in the controller. In the workout _form, the /layouts/formerrors partial is rendered where the local of 'item:' is set as workout, so that the validations provided by the Workout model can display errors when someone forgets to enter the workout name, sets or reps. The new and edit pages of Workout both render the form with locals of workout_group: @workout_group and workout: workout so that the form knows what instance variables it is dealing with. They both contain locals for button_name, but with different values so that a dynamically named button can be rendered on each page. &lt;/p&gt;

&lt;p&gt;The index page for Workouts displays the name of the Workout Group at the top and then iterates over the workouts owned by that group, creating a link with the name of that workout that routes to the show page of that workout, where the user can see the sets and reps of the workout to be performed. Two arguments, @workout_group and workout, are passed into workout_group_workout_path, so that the controller knows both the id of the workout group that the workout belongs to and the id of the workout that is being selected. Below this list, there is a link to create a workout for the workout group that routes to 'new_workout_group_workout_path' and takes the user to the new workout form so that they can create a new workout object for the workout group. Below that, there is a link to go back from the workout list to the workout group show page. The show page for workout contains the name of the workout and its sets and reps, as well as links to edit or delete the workout or to go back to the workout group's workout list or the workout group's show page. &lt;/p&gt;

&lt;p&gt;Conclusion: Pushwith is a Ruby on Rails application that employs a many-to-many relationship between users and workout groups so that users can have many workout groups and workout groups can have many users. It also employs nested routing so that Workout Groups can create their own workouts, and these workouts are only visible within routes for that particular workout group. The Join table between Users and Workout Groups contains a user submittable attribute of mantra so that each User enters an encouraging message to their team when they create or join a workout group. Each model validates the presence of its attributes so that users cannot submit empty forms or forms that are missing values for the attributes of the objects that are being created. Class level ActiveRecord scope methods are included within the Workout Group model so that users can enter search queries to more easily find a workout group that they are looking for, and so that users that are in a particular workout group are able to see a link to leave the workout group on that workout group's show page. Signup, login, and logout features have been implemented in the application so that users can have their own profiles from which to interact with workouts and workout groups. Users are encouraged to use the Omniauth feature to sign in with Google and therefore gain added convenience and additional security. Flash errors and form errors have also been displayed in the application so that users know what is wrong when they submit a form and the form fails to submit. Overall, PushWith offers a variety of features so that each user can have a fulfilling collaborative experience with others at their gym. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Phase Two Project - Dragon Hunter</title>
      <dc:creator>Vikram Chatterjee</dc:creator>
      <pubDate>Wed, 06 Jan 2021 17:57:07 +0000</pubDate>
      <link>https://dev.to/vchatterjee/phase-two-project-dragon-organizer-1c6b</link>
      <guid>https://dev.to/vchatterjee/phase-two-project-dragon-organizer-1c6b</guid>
      <description>&lt;p&gt;Vikram Chatterjee&lt;/p&gt;

&lt;p&gt;Intro&lt;br&gt;
The Dragon Hunter is the premier tool for adventurers who seek to document and hunt the dragons they are looking for or have found in the wild. The Dragon Hunter webpage allows a user to document a new dragon, edit a dragon that they have listed, or delete a dragon. Each user has many dragons and each dragon belongs to and can only be accessed by one specific user. &lt;/p&gt;

&lt;p&gt;Layout&lt;br&gt;
The styling of this website is provided by Materialize, a CSS library available at &lt;a href="http://www.materializecss.com"&gt;www.materializecss.com&lt;/a&gt;. The stylesheet is loaded in the layout page within the head area. A nav bar with options to login or signup, or logout, or to go to the Dragons index, or create a new dragon, based on whether the user is logged in or not, are contained within the nav bar. Also if the user is logged in, the user will be greeted on every page with "Hello, &amp;lt;%=current_user.username%&amp;gt;", so there is no confusion on which user is logged in at any given time. &lt;/p&gt;

&lt;p&gt;Signup &lt;br&gt;
When a user first visits my website, they will be redirected to the GET route for the sign up page. They will be asked to enter a username and password, at which point a POST request will be made to the signup route. A user will be created with the params of username and password and stored into the database. A password digest parameter in the User object uses bcrypt to take the password param and replace it with an encrypted hex value. A session will then be created with a key of :user_id and a value of the user id (user.id). This logs in the user. Then the user will be redirected to the dragons index page, which will initially show an empty list of dragons. &lt;/p&gt;

&lt;p&gt;Login&lt;br&gt;
A link to the login page is only showed on the navbar if the user is not logged in. Like the signup page, a user that is logged in who attempts to go to the /login route will be redirected to /dragons. When a username and password are entered into the login fields, the post '/login' method in the sessions controller will check to see if a user with the given username exists, and then it will use the user.authenticate method from Bcrypt to check to see if the entered password matches the encrypted hex from the password digest of that user. If the login is successful, a session will be created containing the user id,  and the user will be redirected to the Dragons index at '/dragons'. Otherwise, the user will be redirected back to an empty login page. &lt;/p&gt;

&lt;p&gt;A note on sessions&lt;br&gt;
Sessions have been enabled through the application controller. A randomly generated hex value for session secret has been generated in IRB using the securerandom gem and the SecureRandom.hex saved into the .env file and is accessed in the Application Controller. The .env file is not accessible in the repo as it has been filed under gitignore. This way, sessions can not be artificially created by a user in any other way than through the methods that create sessions within the controllers of this program. &lt;/p&gt;

&lt;p&gt;New Dragon&lt;br&gt;
The user will then have the option to click on the New Dragon button at the top right hand corner of the page. They will see a form that allows them to fill in the dragon's name, select a color using radio buttons, and select the dragon's breed, personality, and treasure using drop down menus. The drop down menus are initialized in the layout page using a jquery script towards the bottom of the "head" area. There are 7 different dragon colors for a user to choose between, as well as 5 different breeds, 5 personality types, and 5 different types of treasure. The user must enter/select a value for every attribute, otherwise the Dragon object will not be saved. When all of the attributes for the Dragon are filled out or selected and the user hits submit, a POST request is made to the /dragons route. A local variable named 'dragon' is assigned to a Dragon object that is built using an Active Record association  and if that dragon is saved successfully, a session will be created with the id of that dragon, which allows the website to remember which dragon was most recently viewed or created. The user will then be redirected to the dragons index page, and the name of the dragon that was just created will show up under recently viewed. If the dragon fails to save, the user will hit submit and be redirected to an empty 'new dragon' form. &lt;/p&gt;

&lt;p&gt;Index&lt;br&gt;
When a user requests or is redirected to the Index page, a check is made to see that a user is logged in, and if not, they will be redirected to the signup page. An instance variable, @dragons, is set to Dragon.all, and @dragons is iterated over to see which dragons belong to the user that is logged in. If a dragon's 'user foreign key' matches the id of the currently logged in user, a link to the show page of that dragon will be displayed as the name of the dragon being listed. Above the list of dragon names belonging to the user, a 'Recently Viewed' string of text appears, and presents whatever dragon object is stored in session[:dragon_id]; ie. the dragon whose show page we have most recently viewed. &lt;/p&gt;

&lt;p&gt;Show&lt;br&gt;
The show page lists the name of the dragon as a header, and all of the dragon's attributes (color, breed, personality, treasure) as paragraph tags underneath it. An image of the dragon, based on its color, is rendered beneath the attributes of the dragon. Below the dragon's picture, an edit button is displayed which takes the user to the dragon's edit page, and a delete button is displayed which will delete the dragon and take the user back to the index page where the other dragons are listed. If the user tries to go to the show page of a dragon that does not exist, the redirect if not found method will check to see if a dragon with the corresponding id exists, and if not, the user will be redirected back to /dragons. A session will be created for a specific dragon when its show page is visited, which allows the Recently Viewed string in the index page to list the name of the last dragon that was viewed. &lt;/p&gt;

&lt;p&gt;Edit&lt;br&gt;
The edit form directs the POST request to the route /dragons/&lt;a class="mentioned-user" href="https://dev.to/dragon"&gt;@dragon&lt;/a&gt;
.id and using the line: 'input type="hidden" name="_method" value="patch"' the POST request is changed into a PATCH request. The edit page for the dragon is similar to the new dragon page, except that all of the values for the name and attributes of the dragon are pre-filled in. For the name attribute, this means that the value &lt;a class="mentioned-user" href="https://dev.to/dragon"&gt;@dragon&lt;/a&gt;
.name is listed within the name input field. For the color, the radio button will be checked if the dragon's color matches the value corresponding with the color that the radio button selects for. The code for this is @dragon
.color == "Red" %&amp;gt; so for example if the dragon color is Red, the radio button for Red will be checked. Similarly, the values of the drop-down menus will be preselected with whichever breed, personality, and treasure the dragon has. For example, the drop-down menu will be prepopulated with "Dragonnet" using the code @dragon
.breed == "Dragonnet" %&amp;gt; if the dragon's current breed is Dragonnet. Like with the show page, a dragon can only be edited if the dragon id in question is populated by a dragon that exists. When the submit button is pressed, a patch request will update the dragon's attributes and return the user to the show page for that dragon. &lt;/p&gt;

&lt;p&gt;Dragon and User objects&lt;br&gt;
The Dragon object inherits from ActiveRecord::Base and validates that all of the attributes have been assigned a value in order for the dragon to be saved. The dragon object also has a belongs_to attribute where a dragon belongs to a user. &lt;br&gt;
The User object inherits from ActiveRecord::Base and validates the presence and uniqueness of the username. It also calls on the has_secure_password method from Bcrypt which validates the presence of a password, and encrypts the password param into a password digest. &lt;/p&gt;

&lt;p&gt;User Dragon Relationships&lt;br&gt;
A user is only able to see and interact with dragons that belong to that specific user. In the index page, as was previously mentioned, embedded ruby commands iterate over Dragons.all to see which dragons have a user_id matching the foreign key of the user. If a user tries to go to the show or edit page of a specific dragon, the program will check if that dragon belongs to the user using the foreign key, and if the dragon does not belong to the user, the user will be redirected to /dragons. The current user method checks to see if the session belongs to the user that is logged in, and the redirect if not owner method checks to see if the dragon's owner or user matches the current user. &lt;/p&gt;

&lt;p&gt;Flash Errors&lt;br&gt;
Flash errors have been added to the program, which tell a user the corrections that they need to make in their requests if they have made an error. If a user is logged in and requests the signup or login page, a flash error will come up saying "You are already logged in". Similarly, if a user tries to go to the index page for dragons, or a show or edit page for a specific dragon, and they are not logged in, a flash error will come up saying "You must be logged in." If a user requests the show or edit page of a dragon that does not exist, the error "Dragon not found" will show. If the user attempts to go to the show or edit page of a dragon that belongs to another user, the error message "That is not your dragon!" will show. If a user attempts to go into the edit page of a dragon and delete the name, the error message "Dragon must have a name" will be shown. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Phase One project - Joke App</title>
      <dc:creator>Vikram Chatterjee</dc:creator>
      <pubDate>Fri, 04 Dec 2020 18:26:00 +0000</pubDate>
      <link>https://dev.to/vchatterjee/phase-one-project-joke-app-2f15</link>
      <guid>https://dev.to/vchatterjee/phase-one-project-joke-app-2f15</guid>
      <description>&lt;p&gt;I just finished first project for Flatiron School.  This is a CLI gem that draws data from an API.  I chose the Official Joke API which can be found at “&lt;a href="https://github.com/15Dkatz/official_joke_api%E2%80%9D"&gt;https://github.com/15Dkatz/official_joke_api”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Official Joke Api is an API containing a list of around 400 jokes, including general jokes, programming jokes, and knock-knock jokes. It has endpoint URLS that allow a user to access a random joke, access a joke by type, or access ten random jokes or 10 jokes by type. In my project I decided to focus on the endpoints that give ten jokes. &lt;/p&gt;

&lt;p&gt;I started by setting up an environment that requires “RestClient” to GET information from the end point URLs:&lt;/p&gt;

&lt;p&gt;·         pry (to look into the code for development purposes), and&lt;/p&gt;

&lt;p&gt;·         JSON (to parse the string information from the websites into objects).&lt;/p&gt;

&lt;p&gt;After setting up my “runjokes” file to welcome the user to my app and starting a CLI instance, I began setting up my services:&lt;/p&gt;

&lt;p&gt;·         the Cli class&lt;/p&gt;

&lt;p&gt;·         the Api class, and&lt;/p&gt;

&lt;p&gt;·         my Model class Jokes&lt;/p&gt;

&lt;p&gt;I started with the “Api class” and defined the base-url for the API endpoints.  Then I created a method “self.load_ten_jokes” which employed “RestClient” and “JSON” to get a response from the "/random_ten" endpoint and parse the data from within into an object.&lt;/p&gt;

&lt;p&gt;I iterated over each item in the data and created objects in the Jokes class, initializing each joke by assigning attribute accessors to the keys in each joke hash (id, type, setup, and punchline), and using “.send” to pass the values into their respective keys.&lt;/p&gt;

&lt;p&gt;Then, I passed all 10 of those hashes into a class array “@@all”, which gave me something to represent in the CLI.  I made a reader and a clear method for the “@@all” array and moved onto the CLI.&lt;/p&gt;

&lt;p&gt;In the CLI, I created a main menu which initially asked the user to enter 1 to get 10 random jokes, and created a method to get user input.  Upon getting the user input of 1, I called the Api function to get the jokes, and I iterated over the 10 joke objects in the array, listing them by their order in the array and revealing the setup of each joke.&lt;/p&gt;

&lt;p&gt;Then, I called a “sub_menu” method that asked the user to select a joke in the list (1-10) that they'd like to hear the punchline to.  I made sure that the input was valid and I assigned a variable 'joke' to the number that the user had selected and printed the punchline to that specific joke, which is one level deep into the list of joke objects which I had provided before.&lt;/p&gt;

&lt;p&gt;After revealing a punchline, or telling the user that their input was invalid, I cleared the class array of jokes “@@all” so that 10 new random jokes could be populated into the array.&lt;/p&gt;

&lt;p&gt;I later expanded my code to allow the user to get 10 random jokes by entering 1, or to select jokes by “Type” by entering 2.  I updated the Api class to access endpoints that would yield up to 10 jokes from any of the three different types i.e. general, knock-knock, or programming.&lt;/p&gt;

&lt;p&gt;I correspondingly created three methods in the CLI class which would load a different endpoint by passing in a “Type” argument into the “load_jokes” Api method.  I gave the user the option to choose between these three types, and repeated the process of listing each joke and going into a sub-menu so that the user could select the punchline they would like to hear.&lt;/p&gt;

&lt;p&gt;In the case of the knock-knock joke type, the API had only 5 jokes, so I had to create a dedicated sub-menu that would only accept inputs "1-5" as valid.  After the user hears the punchline or enters an invalid input, the program starts over, and asks the user if they'd like to hear 10 random jokes or select jokes by “Type”.&lt;/p&gt;

&lt;p&gt;As a finishing touch, I customized the get input method to better inform the user whether they should be choosing between “1 or 2”, or “3 to 5” to select joke type, and “1 to 5” for knock-knock jokes, or “1 to 10” for lists of ten jokes.&lt;/p&gt;

&lt;p&gt;My 'Joke App' provides an example for a gem that fetches data from an API, provides a choice for user input at two levels, and provides a list of information that the user can pick from to get more specific details about any specific item on that list.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why am I doing Software Engineering?</title>
      <dc:creator>Vikram Chatterjee</dc:creator>
      <pubDate>Mon, 16 Nov 2020 17:16:03 +0000</pubDate>
      <link>https://dev.to/vchatterjee/why-am-i-doing-software-engineering-2d2m</link>
      <guid>https://dev.to/vchatterjee/why-am-i-doing-software-engineering-2d2m</guid>
      <description>&lt;p&gt;I decided to start my Flatiron journey with Software Engineering because I was always good at logic and learning languages, and I felt that I could use my talents to create amazing things. I minored in philosophy in college because I was always interested in the workings of logic and how it applies to the mind and the world around me. I am also a person who loves problem solving, and it just seems to me that using code to solve the problems of the modern day is right up my alley. &lt;/p&gt;

</description>
      <category>beginners</category>
      <category>motivation</category>
    </item>
  </channel>
</rss>
