├── README.md ├── can-i-use-react-hooks-or-context-api-for-my-final-project.md ├── do-i-need-to-write-spec-tests-for-my-project.md ├── how-can-i-prepare-for-my-project-assessment-or-technical-interview.md ├── how-do-i-get-started-with-my-sinatra-portfolio-project.md ├── how-much-css-should-i-add-to-my-portfolio-projects.md ├── rails-json-api-auth-with-js-front.md └── what-are-some-good-tech-interview-resources.md /README.md: -------------------------------------------------------------------------------- 1 | # FAQs - Resources for Students 2 | 3 | A collection of responses to some FAQs. The title of the file represents the question. Answers represent heavily subjective opinions of the author's own and do not represent the views of any organization or other persons. They do not represent a final authority, or any kind of authority, regarding anything, anywhere, anyhow, under any circumstances :stuck_out_tongue_winking_eye:. 4 | 5 | ## Contributing 6 | 7 | Have a question you'd like to see an answer to? Feel free to submit a PR and ping me -- I'm @howardbdev! :smiley: 8 | 9 | ## Author 10 | 11 | © 2019 by [howardbdev] 12 | 13 | ## License 14 | 15 | This project is licensed under the [MIT License]. 16 | 17 | ## Acknowledgments 18 | 19 | * Thanks to all the students who ask the questions -- and push me to learn more myself! You know who you are. :+1: :wink: 20 | 21 | [howardbdev]:https://github.com/howardbdev 22 | [MIT License]:https://opensource.org/licenses/MIT 23 | -------------------------------------------------------------------------------- /can-i-use-react-hooks-or-context-api-for-my-final-project.md: -------------------------------------------------------------------------------- 1 | ### CAN I USE HOOKS OR CONTEXT API FOR MY FINAL PROJECT? DOES EITHER OF THOSE REPLACE REDUX? 2 | 3 | The short answers are no, no, and not exactly. But please do read the long answer! 4 | 5 | _Of course_ you should look into hooks and implement them in upcoming React apps! And _of course_ you should check out the Context API and see how it feels!!! Of course you should stay up-to-date on the libraries you are putting on your resumes to get jobs!! 6 | 7 | _But_ what is in the curriculum now is still highly relevant and useful. Stick to what's there and learn one thing at a time. Learning about Redux and class components is still an excellent way (I would argue still _the_ way, but that's just an opinion) to learn React! Here is a very brief look at Redux, hooks, and Context API in contrast to what's in the curriculum now... 8 | 9 | #### Redux 10 | 11 | Dan Abramov is the author of Redux, and a member of React's core dev team. He wrote this blog. Go read it, if you like: 12 | 13 | [_You Might Not Need Redux_ by Dan Abramov, the author of Redux](https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367) 14 | 15 | Now that you're angry for having learned and used Redux in a final project that's way too simple for it, settle down, take a breath, and consider _why_ we teach Redux. As Dan points out, Redux is a particular application of a pattern that is not new -- a way to carefully prescribe the way to change state and keep track of those changes. The benefits can be tremendous -- bug tracking, less mismatched-state issues, and a single source of truth for your app. Learning a pattern like this is good for you, not just because you can now use Redux, but because when you get hired as a dev you now understand a pattern you may see in various forms, and you understand why it's useful. 16 | 17 | #### React Hooks 18 | 19 | [When hooks were released](https://www.youtube.com/watch?v=dpw9EHDh2bM), the React core team basically shouted "Do not go back and change everything over to hooks, that's completely unnecessary! Just feel free to start using them if you like them! Oh and give us feedback!" ... So, the vast majority of React code that's in production now is 'hook-less', and full of stateful class components, and some folks may prefer class components anyway, so that syntax is not going anywhere any time soon. Also, learning the ins and outs of class components first will help you both understand and appreciate hooks that much more when you do learn them. Hooks do _not_ replace Redux, and really don't have to do with Redux so much. You can actually have a mix of stateful components and still use Redux, saving your Redux store for state that truly needs to be global and available across multiple components. So Redux and hooks can play nice, as hooks just add state to functional components without the need for class components. I would venture to say that Redux and Context make less sense together. 20 | 21 | #### Context API 22 | 23 | [Check it out](https://reactjs.org/docs/context.html). Do not take this oversimplified analogy as gospel, but you can think of Context as super-lightweight Redux without as many rules, and without as many tools. (I made that up, it's a work in progress.) Using the Context API does allow us to access state from a parent component down several levels without passing props through a bunch of intermediate children components. So does Redux. Redux, however, allows us powerful dev tools and a global state with tightly controlled rules for change. Context, not so much. This does not mean one is better than the other; they are simply different and good for different use cases. Yes, your project is likely too small and uncomplicated to warrant using Redux. Yes, Context could make more sense and involves less syntax. Yes, we still ask that you learn and use Redux. 24 | 25 | #### Not just hooks and Context API: 26 | 27 | This is not really just a question about hooks or Context. What about a MERN stack? How about WatermelonDB? React Native? Vue.js? The list goes on and on, and _of course_ you should be exploring modern frameworks and technologies. But first, learn the curriculum. It's not the end-all, be-all, but it's where you're starting. And it's where you have instructors who can give you detailed help, guidance, and skill assessment.As an organization, and specifically as instructional staff, we're not equipped -- could not really be equipped -- to handle a wide open project assessment where everything is on the table.. Maybe we could, but we couldn't be as picky or thorough. Learn the topics we teach first, and learn them well, while you have staff who can help you do so. Then, take what you've learned and learn more -- focusing on the languages and frameworks that are most appealing to you and fit your style! 28 | 29 | #### ER, SO, _CAN_ I USE [__INSERT_NAME_HERE__] EXTERNAL, EXTRA-CURRICULAR LIBRARY ON MY FINAL PROJECT? 30 | 31 | * YES! --- As long as you are still meeting all the requirements and using the tools we're requesting you use. But you _must_: 32 | - use `create-react-app` to scaffold your app 33 | - use Rails to build a backend API you can write to 34 | - use React 35 | - use Redux 36 | 37 | #### Conclusion, for now 38 | 39 | Understand that this answer, in the context of this curriculum and these projects, is subject to change. It _will_ change. And then it will change again. And again. As you likely have realized by now, and indeed you better have realized by now, programming tools change, evolve, die out, and are born again every day. If you memorized every library available for use for JS today, and mastered every function, method, and feature in existence, tomorrow you would have lots to learn as these things tend to be living, changing structures. That's why learning _how to learn_ is as important, maybe more important, than the granular content of this curriculum. Because it's all going to change sooner or later, and you better keep up! Learn what's there, one step at a time. Build your project to spec as it currently stands, then feel free to explore hooks and Context and MERN stacks and Elixir and anything else that strikes your fancy! 40 | -------------------------------------------------------------------------------- /do-i-need-to-write-spec-tests-for-my-project.md: -------------------------------------------------------------------------------- 1 | ### DO I HAVE TO WRITE TESTS FOR MY PROJECT? 2 | 3 | Short answer: 4 | 5 | Unless specifically stated in the `spec.md` file, the answer is no, you don't have to write tests for your portfolio projects. 6 | 7 | Long answer: 8 | 9 | Practicing writing tests can be a highly useful exercise. We don't focus heavily on the writing aspect of tests in this curriculum, although most labs, of course, contain tests. Practice reading those tests in each lab, and you'll start to get a feel for how to write them. Writing tests will improve your skills and add another tool to your belt -- one that might make you stand out in a job interview. 10 | -------------------------------------------------------------------------------- /how-can-i-prepare-for-my-project-assessment-or-technical-interview.md: -------------------------------------------------------------------------------- 1 | ### HOW CAN I PREPARE FOR MY UPCOMING PORTFOLIO PROJECT ASSESSMENT OR TECHNICAL JOB INTERVIEW? 2 | 3 | #### Make sure you have all the requirements met 4 | 5 | Each portfolio project comes with a `spec.md` (or `spec-js.md`) file containing a list of the requirements. Go through that file and write down where you're meeting each spec within your project. 6 | 7 | For technical job interviews, you may or may not be given an assignment to complete beforehand. Make sure to complete it well ahead of time (duh?), and review your work so you're comfortable explaining it. Record yourself explaining your solution, go eat lunch, then sit and listen to yourself. Do you make sense? Do you sound like you know what your talking about? _Do_ you know what you're talking about? Just kidding, of course you do! And it's ok to feel some imposter syndrome symptoms, but push your way past them and carry on! 8 | 9 | ### Practice talking about your code 10 | 11 | One of the harder aspects of learning to code is learning to talk about code. The words can be confusing and have different meanings, and just because you can write good code doesn't mean you can talk good code. Some tips: 12 | 13 | **_Rubber ducking_** - Have a friend, significant other, pet, stuffed animal, or, well, you know-, sit with you while you explain your code to them. Teach them about the code you've written and the underlying concepts. If it's a person, have them give you feedback about your clarity and presentation. If it's a pet or rubber duck, you can ask for feedback, and if you get it, get help. Just kidding. 14 | 15 | **_Record yourself_** - This may seem weird at first, but it's a powerful tool. Again, teach yourself a concept and explain how you're applying it in your code. Go through the spec file and explain out loud each part of your implementation. Stick to short sessions - 5 minutes at a time. Then listen to your recording. Where are you struggling to come up with the right words? You may find areas where your articulation is lacking, and you may uncover some gaps in understanding that you can go study up on! _Hint:_ It may help to give yourself some time between recording and listening, so go for a walk or watch a tv show, or even come back the next day before you listen to yourself. This may give you some fresh perspective. 16 | 17 | **_Write a blog_** - Another very powerful tool. Writing an explanation of something uses a different part of the brain than speech (or so I'm told). Once you've uncovered some areas you don't understand, and/or some areas you're excited to learn more about, write a blog about it! Go nuts! Add examples, references, and funny GIFs! There is no limit to the number of blogs you can write, and a writing a technical blog will almost certainly result in your learning something new. Plus, sharing a series of well-written blogs to a potential job interviewer can be a great way to showcase your willingness to learn and articulate new concepts. 18 | 19 | ### Prepare personally and technically 20 | 21 | **_Personal preparation_** - Imagine this assessment is a technical interview for a job. In the case of an actual technical interview for a job, well, you get it... Treat it as such. Get plenty of sleep. Eat well. Relax, do whatever you do to get your mind to a good place. If you get anxious about these interviews, that's fine! Use your anxiety coping mechanisms, or learn new ones. Groom yourself, and dress as you would for a job interview (we don't need business suits or formal wear, but look presentable). The tech world is often business casual, but wear something you feel good in, something that gives you confidence. 22 | 23 | **_Technical preparation_** Review the previous section - make sure you've talked about your code. Make sure your code is fresh in your head. If you've gone ahead in the curriculum, that's fine, but go back and spend some time reviewing your project. "Oh, wait, I haven't looked at this in, like, a month..." is the interviewer's cue to turn up the heat! It's also rude, as it shows a lack of respect for the interviewer's time. Many technical interviews will come with code to review - maybe in the form of an assignment or challenge, but at least a language and/or library to focus on. Would you walk into a JS tech interview and say, "Oh, I've only been working in Ruby for a while, so let me think about JS syntaxes here for a minute..."? What languages and libraries were listed on the job description? Make sure you review them and your knowledge of them. Google for tech interview questions around the specific language or libraries you'll be interviewing about. Build a small app using those technologies. Record yourself explaining some core concepts. 24 | -------------------------------------------------------------------------------- /how-do-i-get-started-with-my-sinatra-portfolio-project.md: -------------------------------------------------------------------------------- 1 | ### HOW DO I GET STARTED WITH MY SINATRA PORTFOLIO PROJECT? 2 | 3 | #### DO NOT WRITE CODE YET!!!! #### 4 | 5 | ###### Pre-coding work 6 | 1. Ideate! What do you want to build? 7 | - choose a domain you're familiar with! 8 | - choose a domain you care about 9 | 2. Wireframing & User Stories 10 | - Write down your models, their attributes, and their associations 11 | - As a user, I can ..... 12 | - A user should be able to ..... 13 | - What does your app _do_? 14 | 3. Design your MVP = 'Minimum Viable Product' vs. what are my 'stretch goals' 15 | - Stretch goals - bonus features you want but don't need 16 | 17 | #### NOW, WE CODE! * but NO controllers or views yet * 18 | 19 | 4. Build your models 20 | - Migrations 21 | - Model classes 22 | - Associations (& validations) 23 | 24 | 5. Test your models and associations in the console 25 | - create some seed data 26 | - adjust migrations as needed 27 | 28 | #### NOW, CONSIDER CONTROLLERS AND VIEWS 29 | 30 | 6. Start with your ApplicationController helpers - `#logged_in?` and `#current_user` 31 | - add your login/signup/signout routes 32 | 33 | 7. Build out controller routes for other models (add a controller for each model) 34 | 35 | 8. Build views and controller actions based on the flow of your app, one step at a time, testing as you go! 36 | - Use `shotgun` and `pry` (or `raise`/`inspect`) all the time! 37 | 38 | 39 | ###### Using the corneal gem 40 | 41 | You are welcome to use the [corneal gem]. However, you should understand what it's doing. Remove any folders and files you're not using. For example, if you're not going to write any tests, delete the `spec` folder. 42 | 43 | [corneal gem]:https://github.com/thebrianemory/corneal 44 | -------------------------------------------------------------------------------- /how-much-css-should-i-add-to-my-portfolio-projects.md: -------------------------------------------------------------------------------- 1 | ### HOW MUCH TIME SHOULD I SPEND ON STYLING MY APPLICATION? 2 | 3 | Short answer: 4 | 5 | Don't worry too much about CSS. As soon as your app meets requirements, schedule your assessment. Good idea to add enough style to make it look presentable, but that's up to you. 6 | 7 | Long answer: 8 | 9 | CSS is not the focus of this particular curriculum. Yes, it's important, but right now our goal is that you learn to build clean, robust applications that work. Better that you have a black and white, style-free app that works and that you can explain than to have a beautifully styled app that doesn't meet requirements, doesn't work, or that you don't understand. 10 | 11 | That said, once your app is functional and meets requirements, feel free to go nuts with style! How much or how little you wish to style your app depends on you! How fast do you want to move through the course? Whether it's worth the time investment is up to you. It won't affect whether you move along in the course. We're assessing your back end not your front end (unless you're in JS, then we're assessing your front end, too, but still not so much your style). Remember you can always go back and brush up/style up your portfolio projects once you've completed the curriculum. 12 | 13 | **Regardless of your timing goals, your understanding is far more important than speed.** Sprinting through the course without properly absorbing and digesting the material won't do anyone any good. We want you to master the concepts and be a "no-brainer" hire when you're looking for a developer job. _That's_ our goal. So you _must_ take the time you need to understand the course material as you go along. 14 | 15 | The suggestions here about not spending too much time with styling are framed within the following context: 16 | 17 | - Most students want to complete the course ASAP. 18 | - You can go back and style up your apps in the future, especially once you've learned a ton more material (including JavaScript). 19 | - Once you've completed your Rails and JS projects, you'll have a better perspective about which projects you want to highlight to interviewers. 20 | 21 | None of these means that you _can't_ or _shouldn't_ style up your app!! If you don't mind taking the time to do it, then go for it - 100%!! When you do take the time to style an app, you'll learn valuable CSS lessons. So if you have the time to do it, and you want to, then go for it! It's just not required for the project in terms of your assessment. 22 | -------------------------------------------------------------------------------- /rails-json-api-auth-with-js-front.md: -------------------------------------------------------------------------------- 1 | # Authentication with a Rails API 2 | _Session management with a familiar tool -- Rails's `session`!_ 3 | 4 | [Rails JSON API] backend? 5 | [JavaScript] frontend? 6 | Want users to be able to sign up, log in, and log out? Maintain their session even after closing browser tab? 7 | 8 | Criteria for this blog to be applicable: 9 | 10 | - Rails JSON API backend built with `--api` flag 11 | - Some kind of JS frontend that sends AJAX requests (could be Vanilla JS, React, or some other JS framework) 12 | - JS frontend must have an identifiable domain that can be whitelisted 13 | 14 | _Follow along to build a Rails backend that can handle auth. Up to you to provide the frontend of your choice._ 15 | 16 | Run `rails new secrets-backend --api`. 17 | Of course, "secrets-backend" is the name of your Rails app. 18 | 19 | The `--api` flag removes a bunch of middleware from the generation of the Rails application. It also changes the configuration of some of the generators. We are saying, "Hey Rails, how's it going? For this app you can take it easy -- no need to include middleware for views since we're just going to be returning JSON." 20 | 21 | If we want auth, though, it removes a little _too much_ middleware... more on that later.. Let's continue: 22 | 23 | We know we're going to want our Rails server to process AJAX requests from external domains, which means we need to enable [Cross Origin Resource Sharing], or CORS. 24 | 25 | In the `Gemfile`, comment in the line that says 26 | 27 | `gem 'rack-cors'` 28 | 29 | Then run `bundle install` again. 30 | 31 | Enabling the `rack-cors` gem is not enough -- we also need to adjust the CORS configuration. So let's open `config/initializers/cors.rb`. First, uncomment this code: 32 | ```ruby 33 | Rails.application.config.middleware.insert_before 0, Rack::Cors do 34 | allow do 35 | origins 'example.com' 36 | 37 | resource '*', 38 | headers: :any, 39 | methods: [:get, :post, :put, :patch, :delete, :options, :head] 40 | end 41 | end 42 | ``` 43 | 44 | On the line that starts with `origins`, we're telling Rails what domains to accept AJAX requests from. Let's start by replacing `'example.com'` with the the catch-all character `*`. This means, "Hey Rails, please accept AJAX requests from all domains." So now it should look like this: 45 | 46 | ```ruby 47 | Rails.application.config.middleware.insert_before 0, Rack::Cors do 48 | allow do 49 | origins '*' 50 | 51 | resource '*', 52 | headers: :any, 53 | methods: [:get, :post, :put, :patch, :delete, :options, :head] 54 | end 55 | end 56 | ``` 57 | 58 | Keep this file open. We're going to make more CORS configuration changes soon. We should also pause for a moment to ensure understanding around the term AJAX. The way it's being used in this blog, AJAX refers to requests being sent to our backend from our JS code. AJAX stands for [Aysychronous JavaScript and XML], but at this point should really be AJAJ, since we're getting JSON responses instead of XML responses from our Rails server. Some might distinguish AJAX from the [Fetch API], but we're clomping them together here. Bottom line, when we say "make a fetch request" and "make an AJAX request" we mean the same thing in this blog. Please do not confuse with [jQuery's `.ajax()`] method, although that would fall into the same category. Sheesh. 59 | 60 | With our basic CORS config setup, let's pause and scaffold out a user model we can test with. Since this is a blog on auth, let's give users email and password attributes. Let's use the `bcrypt` gem to hash our passwords, so head back to the Gemfile and tag it in: 61 | ```ruby 62 | gem 'bcrypt', '~> 3.1.7' 63 | ``` 64 | _Note: The version number seen here is from running a `rails new` command with Rails 6.0.0... of course versions change and might be different for you if you're running the same command in the future... or in the past... or with different versions..._ 65 | 66 | Run `bundle install`. 67 | 68 | This is not the blog that explains how to use `bcrypt`. If you're unsure how it works, go find one or better yet, [read the docs]. 69 | 70 | Let's make some users. Run 71 | 72 | `rails g scaffold user name email password_digest` 73 | 74 | followed by 75 | 76 | `rails db:migrate` 77 | 78 | To ensure we have hashed passwords and access to our `#password` setter, as well as the `#authenticate` method, let's add 79 | `has_secure_password` to our `User` model: 80 | ```ruby 81 | # inside app/models/user.rb 82 | class User < ApplicationRecord 83 | has_secure_password 84 | end 85 | ``` 86 | 87 | And let's create a couple users. In `db/seeds.rb`: 88 | ```ruby 89 | User.create(name: "Francine", email: "francine@francine.com", password: "francine") 90 | User.create(name: "Franklin", email: "franklin@franklin.com", password: "franklin") 91 | ``` 92 | 93 | Run 94 | 95 | `rails db:seed` followed by `rails c` so we can check our users exist: 96 | 97 | ``` 98 | // ♥ rails c 99 | User.Running via Spring preloader in process 12138 100 | couLoading development environment (Rails 6.0.0) 101 | 2.6.1 :001 > User.count 102 | (0.4ms) SELECT sqlite_version(*) 103 | (0.4ms) SELECT COUNT(*) FROM "users" 104 | => 2 105 | 2.6.1 :002 > User.first 106 | User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] 107 | => # 108 | ``` 109 | This is looking good. 110 | 111 | We have scaffolded a user resource, added `bcrypt`, and created some seed data. Let's have a look at what Rails's scaffold generator gives us for a controller since we used the `--api` flag. Notice we have a two private methods, `#set_user` to grab the user when the `:id` exists in the URL, and `#user_params` to enforce ["strong params"]. Notice the absence of `#new` and `#edit` actions, since we'll now be relying on JS for all HTML generation, including forms. Finally, notice each controller action renders a response in JSON format, which is what we want. Ideally, we ought to use a serializer to format and dictate exactly what to include in our JSON responses. Options include gems like [Jbuilder] and [FastJsonapi]. We could also build our own custom serializer, build inline response hashes, or just use the [`#to_json`] method, which is what we'll do in this example for simplicity. 112 | 113 | The `#index` action is quite simple here: 114 | ```ruby 115 | # GET /users 116 | def index 117 | @users = User.all 118 | 119 | render json: @users.to_json(only: [:id, :name, :email]), status: :ok 120 | end 121 | ``` 122 | _Note: Of course, it may not make sense to have a users index, but this is just for the demo..._ 123 | 124 | Let's run our Rails server and see what we get. We've left the default settings, so our Rails server is running on `http://localhost:3000`. _Another note here: It might be a good idea to namespace your API with "api" and/or a version number, such as `http://localhost:3000/api/v1/users`. Again, not the focus of this blog, so we're sticking to bare bones on this front._ Navigating to `http://localhost:3000/users` gives us our JSON response: 125 | 126 | ```json 127 | [ 128 | { 129 | "id": 1, 130 | "name": "Francine", 131 | "email": "francine@francine.com" 132 | }, 133 | { 134 | "id": 2, 135 | "name": "Franklin", 136 | "email": "franklin@franklin.com" 137 | } 138 | ] 139 | ``` 140 | 141 | Let's see what we've got when our JS frontend talks to our Rails backend! What frontend? Your frontend might be a [React] application or plain old Vanilla JS. For the sake of this blog, we'll be serving our JS frontend from `http://localhost:8000`. Let's see what happens when we fire off a `fetch` request to get our users. 142 | 143 | ```javascript 144 | fetch("http://localhost:3000/users") 145 | .then(response => response.json()) 146 | .then(console.log) 147 | ``` 148 | 149 | And we check our console... success! 150 | ``` 151 | (2) [{…}, {…}] 152 | 0: {id: 1, name: "Francine", email: "francine@francine.com"} 153 | 1: {id: 2, name: "Franklin", email: "franklin@franklin.com"} 154 | length: 2 155 | __proto__: Array(0) 156 | ``` 157 | 158 | Done. Now we've got a JS frontend communicating with a Rails JSON API backend. What did we come here for, again? Oh yeah, auth. We're not close to done. OK, what is auth, anyway? It's actually two things: 159 | 160 | **Authentication**: Ensure the user is who the user claims to be. 161 | 162 | **Authorization**: Once we've established the user's identity, ensure the user is allowed to do the thing the user is trying to do. 163 | 164 | This blog focuses on authentication. _(There is a small authorization example at the end.)_ 165 | 166 | _Note: Regarding semantics around the word "session": In this blog, we've mentioned user sessions and Rails's `session`. Session used with no code highlighting just refers to a session, as in a period of time between a user logging in and logging out. When we say `session` as a code snippet, we're referring to a tool provided by Rails. We say "Rails's `session` hash". It's not really just a hash, although it kinda looks like it the way we use it. In Rails controllers, `session` is actually an instance of the [`ActionDispatch::Request::Session`] class. Technically, it's an invocation of `self.session` -- the getter method for said `ActionDispatch::Request::Session` instance. Too much? OK, we'll bend the language a bit and stick with "`session` hash", since that's what it feels like._ 167 | 168 | To keep track of user sessions, we need signup, login, and logout functionality. Let's start with signup. Signup represents creating a user. From there, it's up to you, the developer, whether you send the user back to a login page or log the user in automatically on signup. Let's go with the latter for this example. 169 | 170 | Suppose we have a signup form that gathers a user's name, email, and password. We put the attributes into an object and pass it to the function we're using to send our POST request. We might put together a request that looks something like this: 171 | ```javascript 172 | // suppose our `userData` is `{user: {name: "Felicia", email: "felicia@felicia.com", password: "felicia"}}` 173 | fetch("http://localhost:3000/users", { 174 | method: "POST", 175 | headers: { 176 | "Content-Type": "application/json", 177 | "Accept": "application/json" 178 | }, 179 | body: JSON.stringify(userData) 180 | }) 181 | .then(response => response.json()) 182 | .then(console.log) 183 | ``` 184 | _Note: In this example `userData` has a top-level key of `:user`, which is apparently required by our `#user_params` method -- the ["strong params"] feature used to whitelist attributes for creating or updating model records. We don't actually need the `:user` key, though, since Rails will [wrap params] by default. True to form with Rails, of course, this feature is totally configurable. In our example, though, we just need to update `#user_params` so the whitelisted attributes include `password` rather than `password_digest`, which is what we got from the scaffold generator. Beware the scaffold generator; the resulting code may be too much, too little, or not exactly what you need..._ 185 | ```ruby 186 | def user_params 187 | params.require(:user).permit(:name, :email, :password) 188 | end 189 | ``` 190 | 191 | Anyway, now let's see what the console shows us: 192 | ``` 193 | {id: 5, name: "Felicia", email: "felicia@felicia.com"} 194 | ``` 195 | Success! But wait, we're talking about auth...? Right now, even though we can create a user, there is no session. So neither the frontend nor the backend is aware of who the current user is. Let's fix that. 196 | 197 | Let's head back to our backend to continue our authentication configuration. Hopefully you are familiar with using a basic auth setup on a full Rails app -- one where Rails is rendering the views via `.erb` files. Basically, we're going to use [Rails's `session` hash] to store an identifiable bit of info about the current user after signing up or logging in. Logging out involves clearing the `session` hash using Rails's `#reset_session` method. 198 | 199 | In the `users#create` action of a full Rails app, you may recall seeing code something like this: 200 | ```ruby 201 | # POST /users 202 | def create 203 | @user = User.new(user_params) 204 | if @user.save 205 | # the act of logging in is really just adding a key/value pair to the session hash 206 | session[:user_id] = @user.id 207 | redirect_to @user 208 | else 209 | flash[:message] = "Failed to create user" 210 | render :new 211 | end 212 | end 213 | ``` 214 | Likewise, once we have users signed up, perhaps we use a `SessionsController` to handle logging in and out, something like: 215 | 216 | ```ruby 217 | # POST /login 218 | def login 219 | @user = User.find_by(email: params[:user][:email]) 220 | if @user && @user.authenticate(params[:user][:password]) 221 | session[:user_id] = @user.id 222 | # then redirect to a landing page or whatever 223 | else 224 | # communicate to the user their credentials are invalid, maybe using a flash message 225 | end 226 | end 227 | 228 | # POST /logout 229 | def logout 230 | reset_session 231 | # redirect back to a home or landing page 232 | end 233 | ``` 234 | 235 | Since our controllers are now serving JSON, let's tweak `users#create`, `sessions#login` and `sessions#logout` accordingly: 236 | 237 | ```ruby 238 | # in UsersController 239 | def create 240 | @user = User.new(user_params) 241 | if @user.save 242 | # still just adding a key/value pair to the session hash 243 | session[:user_id] = @user.id 244 | render json: @user.to_json(only: [:id, :name, :email]), status: :created 245 | else 246 | render json: { 247 | error: @user.errors 248 | }, status: :unprocessable_entity 249 | end 250 | end 251 | 252 | # in SessionsController 253 | def login 254 | @user = User.find_by(email: params[:user][:email]) 255 | if @user && @user.authenticate(params[:user][:password]) 256 | session[:user_id] = @user.id 257 | render json: @user.to_json(only: [:id, :name, :email]), status: :ok 258 | else 259 | render json: { 260 | error: "Invalid credentials" 261 | }, status: :unauthorized 262 | end 263 | end 264 | 265 | def logout 266 | reset_session 267 | render json: { 268 | message: "Successfully logged out" 269 | }, status: :ok 270 | end 271 | ``` 272 | Here are some likely helper methods we might include in `ApplicationController`: 273 | 274 | ```ruby 275 | def current_user 276 | User.find_by(id: session[:user_id]) 277 | end 278 | 279 | def logged_in? 280 | !!current_user 281 | end 282 | ``` 283 | One more controller action that will probably prove useful is an action to get the current user if there is one. This would likely be an endpoint we'll hit as soon as the app loads, so on `"DOMContentLoaded"` with JS or in `componentDidMount()` or `useEffect()`, if you're using [React]. Something like: 284 | 285 | ```ruby 286 | # in `SessionsController` or `UsersController`, whichever you prefer 287 | def get_current_user 288 | if logged_in? 289 | render json: current_user.to_json(only: [:id, :name, :email]), status: :ok 290 | else 291 | render json: { 292 | message: "No one is currently logged in" 293 | }, status: :unauthorized 294 | end 295 | end 296 | ``` 297 | We can hit this controller action with an AJAX call like this: 298 | ```js 299 | fetch("http://localhost:3000/get_current_user", { 300 | method: "GET", 301 | headers: { 302 | "Content-Type": "application/json", 303 | "Accept": "application/json" 304 | } 305 | }) 306 | .then(response => response.json()) 307 | .then(console.log) 308 | ``` 309 | 310 | So let's put this together with a new user and see what happens. The test will be as follows: we'll send a POST request to sign up a new user (we could just as easily use login, either will do). With the code we added to `users#create`, adding the user's id to the `session` hash, we should be logged in and the current user should appear in the console on a page refresh (notice in our `POST '/users'` and `GET '/get_current_user'` requests shown earlier we just passed `console.log` as our callback to the second `.then()`). 311 | 312 | But alas, what we're still seeing is: 313 | ``` 314 | the current user is {message: "No one is currently logged in"} 315 | ``` 316 | 317 | This is because the middleware Rails uses to include the `session` is removed due to the `--api` flag. Here are the steps to get it back: 318 | 319 | Head to `config/application.rb`. Inside the `Application` class, add these two lines of code: 320 | 321 | ```ruby 322 | config.middleware.use ActionDispatch::Cookies 323 | config.middleware.use ActionDispatch::Session::CookieStore, key: '_cookie_name', expire_after: 7.days, httponly: true 324 | ``` 325 | 326 | We are bringing back middleware for cookies and sessions... who doesn't love a good session with some cookies?! Options for [`CookieStore`] include: 327 | - `:key` is just that -- the key the cookie is stored under in the browser. 328 | - `:expire_after` allows us to set an expiration time for our cookies (the more sensitive the app's data, the shorter the cookie's lifespan should be). 329 | - `:httponly` is an extra option to ensure our cookies are less vulnerable to an [XSS attack]. 330 | - `:secure` Defaults to `false`, but add `secure: true` only if running on HTTPS. `localhost` does not, so it's not included in the example code. 331 | 332 | Now we have to explicitly tell Rails to use the middleware we just turned back on. We can do this on a controller-specific basis, but the easiest way is to just add to `ApplicationController`: 333 | 334 | ```ruby 335 | include ActionController::Cookies 336 | ``` 337 | 338 | Since all other controllers inherit from `ApplicationController`, they all get access to the tools we need now. Since we've made configuration changes, restart your Rails server. Now, all is well with the world. Or, is it? 339 | 340 | _Goes back, fills out sign up form, submits, annnddddd..._ 341 | 342 | ``` 343 | the current user is {message: "No one is currently logged in"} 344 | ``` 345 | 346 | Oh, no! Still not working! You may have noticed something... we've 'turned on' Rails's `session` and cookies abilities, but so what? Rails used to control the entire app, frontend and backend, so it was magically self-aware of what it included in its own requests and renderings and that it should include session cookies in its responses and look for them in incoming requests... this may be an oversimplification, but the goal here is not to dive down the rabbit hole of cookies here. Instead, let's be practical: we need Rails to include session cookies in its requests, which we are doing now. But we also need Rails to look for those cookies in _incoming_ AJAX requests now, too -- requests coming from external domains, i.e. from our browser. We need to tell Rails two things: first, what domain(s) is(are) authorized to make AJAX requests, and second, to look for session cookies on those incoming requests. Back to `config/initializers/cors.rb`: 347 | 348 | ```ruby 349 | Rails.application.config.middleware.insert_before 0, Rack::Cors do 350 | allow do 351 | # 'whitelist' our origin, which for this example is `localhost:8000` 352 | origins 'http://localhost:8000' 353 | 354 | resource '*', 355 | headers: :any, 356 | methods: [:get, :post, :put, :patch, :delete, :options, :head], 357 | credentials: true 358 | # ^^ the `credentials: true` key/value pair tells Rails, "please look for incoming session cookie information!" 359 | end 360 | end 361 | ``` 362 | Ok, we must be getting close... right? 363 | _Restarts Rails server, fills out signup form with Darlene's info, refreshes to see if we're logged in, and ..._ 364 | 365 | ``` 366 | the current user is {message: "No one is currently logged in"} 367 | ``` 368 | 369 | What gives? So not only did we need to tell Rails to look out for session cookie info, we also need to tell _JavaScript_ to _include_ said cookies in our AJAX requests! BUT WAIT... Rails is, well, [Ruby on Rails], with tons of helper methods and "magic", and [JavaScript] is, well, not Rails! And we've disconnected our application so that we have a totally separate frontend and backend. So how do we take this "final" step? We Google, "how to include credentials in a fetch request"... And if we're me, we tack "MDN" to the front of that, so "MDN how to include credentials in a fetch request". 370 | 371 | The first hit is [MDN's "Using Fetch"] article, where it says 372 | 373 | > ``` 374 | > By default, `fetch` won't send or receive any cookies from the server, resulting in unauthenticated requests if the site relies on maintaining a user session (to send cookies, the credentials `init option` must be set). 375 | >``` 376 | > _source:_ https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch 377 | 378 | If we click ["init option"], we're taken to MDN's _[WindowOrWorkerGlobalScope.fetch()]_ article, where it says 379 | 380 | >``` 381 | >init | Optional 382 | > An options object containing any custom settings that you want to apply to the request. The possible options are: 383 | >``` 384 | > _...a whole bunch of options are described here here, including (hopefully) familiar properties such as `headers` and `method`, but `credentials` is what we want:_ 385 | >``` 386 | > credentials: The request credentials you want to use for the request: omit, same-origin, or include. To automatically send cookies for the current domain, this option must be provided. Starting with Chrome 50, this property also takes a FederatedCredential instance or a PasswordCredential instance. 387 | >``` 388 | >_source:_ https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters 389 | 390 | To get a little more info, we might head to [MDN's "Request.credentials"] article, and we find out a little more about the `include` value of the `credentials` option: 391 | >``` 392 | >include: Always send user credentials (cookies, basic http auth, etc..), even for cross-origin calls. 393 | >``` 394 | >_source:_ https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials 395 | 396 | BINGO! 397 | 398 | So now, we just need to add `credentials: "include"` to all our AJAX calls, like so: 399 | 400 | ```javascript 401 | // suppose our `userData` is `{user: {name: "Mo", email: "mo@mo.com", password: "password"}}` 402 | fetch("http://localhost:3000/users", { 403 | method: "POST", 404 | credentials: "include", 405 | headers: { 406 | "Content-Type": "application/json", 407 | "Accept": "application/json" 408 | }, 409 | body: JSON.stringify(userData) 410 | }) 411 | .then(response => response.json()) 412 | .then(console.log) 413 | ``` 414 | and 415 | ```js 416 | fetch("http://localhost:3000/get_current_user", { 417 | method: "GET", 418 | credentials: "include", 419 | headers: { 420 | "Content-Type": "application/json", 421 | "Accept": "application/json" 422 | } 423 | }) 424 | .then(response => response.json()) 425 | .then(console.log) 426 | ``` 427 | 428 | And now, when we go to sign Mo up, then refresh the page and check the console: 429 | 430 | ``` 431 | the current user is {id: 10, name: "Mo", email: "mo@mo.com"} 432 | ``` 433 | 434 | Yaaaaaayyy!!!! 435 | 436 | If we've defined our logout route in `config/routes.rb` as 437 | 438 | `post "/logout", to: "sessions#logout"` 439 | 440 | then we could test logout functionality: 441 | 442 | ```js 443 | fetch("http://localhost:3000/logout", { 444 | method: "POST", 445 | credentials: "include", 446 | headers: { 447 | "Content-Type": "application/json", 448 | "Accept": "application/json" 449 | } 450 | }) 451 | .then(response => response.json()) 452 | .then(console.log) 453 | ``` 454 | We should see in our console: 455 | ```js 456 | {message: Successfully logged out} 457 | ``` 458 | Refresh, and watch the console again: 459 | ``` 460 | the current user is {message: "No one is currently logged in"} 461 | ``` 462 | _Wait, why did we [use POST as our HTTP method for logout]? What about GET, or maybe DELETE? Good question. Although we're not deleting a record from a database, it does seem like we are "deleting" something, albeit an abstract something -- the user session. However if we [compare MDN's descriptions of HTTP methods] notice POST's description is more open to what's happening here. DELETE seems to imply we're deleting an actual resource, which we're not. Of course, you're encouraged to [read] [more] [about it] and decide which HTTP method to use for logout on your app._ 463 | 464 | And that's it! Now your Rails responses will include an HTTP-only cookie with session info. And each AJAX request with `credentials: "include"` will send that cookie back to be authenticated. 465 | 466 | Of course, instead of just logging responses to the console, we'd likely do something with them on the front end. Maybe stash the current user into whatever state management system we're using, whether it's React state, [Redux], or any other flavor or JS we like. And, of course, since we're building an SPA, instead of refreshing the page to get results, we'll want to update our DOM by invoking the proper methods and functions we've built to do so on our front end. Which is a whole other thing, altogether. 467 | 468 | For authorization, protect your controller actions. For example, suppose we've added a `Secret` model and associations such that a user `has_many :secrets` and a secret `belongs_to :user`. If we don't want anyone but the secret's owner to see a secret, then in `SecretsController`: 469 | ```ruby 470 | def show 471 | @secret = Secret.find(params[:id]) 472 | if @secret.user == current_user 473 | render json: @secret.to_json(only: [:content, :id]), status: :ok 474 | else 475 | render json: { 476 | error: "Those aren't your secrets!" 477 | }, status: :unauthorized 478 | end 479 | end 480 | ``` 481 | 482 | What do you think? That's pretty much it! Comments and feedback are welcome. But be nice. 483 | 484 | ### Some common config errors you might encounter: 485 | 486 | If you leave the wildcard `'*'` as your origin and add `credentials: true`, Rails won't even allow the server to run: 487 | 488 | ``` 489 | Allowing credentials for wildcard origins is insecure. Please specify more restrictive origins or set 'credentials' to false in your CORS configuration. (Rack::Cors::Resource::CorsMisconfigurationError) 490 | ``` 491 | 492 | Adding `credentials: "include"` to your AJAX requests without properly whitelisting your origin(s) will trigger a CORS error when the request is made: 493 | ``` 494 | Access to fetch at 'http://localhost:3000/get_current_user' from origin 'http://localhost:8000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. 495 | ``` 496 | Whitelisting origin(s), adding `credentials: "include"` to AJAX requests, but failing to add `credentials: true` in your Rails CORS config results in a slightly different error: 497 | 498 | ``` 499 | Access to fetch at 'http://localhost:3000/users' from origin 'http://localhost:8000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. 500 | ``` 501 | 502 | 503 | [Rails JSON API]: https://guides.rubyonrails.org/api_app.html 504 | [React]: https://reactjs.org 505 | [Jbuilder]: https://github.com/rails/jbuilder 506 | [`#to_json`]: https://apidock.com/rails/ActiveRecord/Serialization/to_json 507 | [Cross Origin Resource Sharing]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS 508 | [Aysychronous JavaScript and XML]: https://developer.mozilla.org/en-US/docs/Glossary/AJAX 509 | [jQuery's `.ajax()`]: https://api.jquery.com/jquery.ajax/ 510 | [read the docs]: https://github.com/codahale/bcrypt-ruby 511 | [wrap params]: https://api.rubyonrails.org/v5.2.3/classes/ActionController/ParamsWrapper.html 512 | ["strong params"]: https://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html 513 | [`ActionDispatch::Request::Session`]: https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/request/session.rb 514 | [`#reset_session`]: https://apidock.com/rails/ActionController/Base/reset_session 515 | [Fetch API]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API 516 | [FastJsonapi]: https://github.com/Netflix/fast_jsonapi 517 | [`CookieStore`]: https://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/Session/CookieStore.html 518 | [Ruby on Rails]: https://rubyonrails.org 519 | [JavaScript]: https://developer.mozilla.org/en-US/docs/Web/JavaScript 520 | [Redux]: https://redux.js.org 521 | [MDN's "Using Fetch"]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch 522 | ["init option"]: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters 523 | [WindowOrWorkerGlobalScope.fetch()]: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters 524 | [HTTPS]: https://www.globalsign.com/en/blog/the-difference-between-http-and-https/ 525 | [MDN's "Request.credentials"]: https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials 526 | [XSS attack]: https://humanwhocodes.com/blog/2009/05/12/cookies-and-security/ 527 | [Rails's `session` hash]: https://guides.rubyonrails.org/security.html#sessions 528 | [use POST as our HTTP method for logout]: https://softwareengineering.stackexchange.com/questions/196871/what-http-verb-should-the-route-to-log-out-of-your-web-app-be 529 | [compare MDN's descriptions of HTTP methods]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods 530 | [read]: https://stackoverflow.com/questions/15098392/which-http-method-should-login-and-logout-actions-use-in-a-restful-setup?noredirect=1&lq=1 531 | [more]: https://stackoverflow.com/questions/3521290/logout-get-or-post/14587231#14587231 532 | [about it]: https://stackoverflow.com/questions/7140074/restfully-design-login-or-register-resources 533 | -------------------------------------------------------------------------------- /what-are-some-good-tech-interview-resources.md: -------------------------------------------------------------------------------- 1 | ### WHAT ARE SOME GOOD RESOURCES FOR TECHNICAL INTERVIEW PREPARATION? 2 | 3 | ### All/mixed Languages 4 | 5 | [10 Code Challenge Sites](https://medium.com/coderbyte/the-10-best-coding-challenge-websites-for-2018-12b57645b654) 6 | 7 | ### JavaScript 8 | 9 | [LinkToptal 37 JS IT Qs](https://www.toptal.com/javascript/interview-questions) 10 | 11 | [Dev Bits](https://medium.com/dev-bits/a-perfect-guide-for-cracking-a-javascript-interview-a-developers-perspective-23a5c0fa4d0d) 12 | 13 | [OLIQ.com](https://www.onlineinterviewquestions.com/es6-interview-questions/#.W5ZxwZNKjow) 14 | 15 | [JS Scene](https://medium.com/javascript-scene/10-interview-questions-every-javascript-developer-should-know-6fa6bdf5ad95) 16 | 17 | 18 | [Eric Elliott on Medium](https://medium.com/@_ericelliott) 19 | 20 | [Fun Fun Function with MPJ on YouTube](https://www.youtube.com/channel/UCO1cgjhGzsSYb1rsB4bFe4Q) 21 | 22 | #### Understanding Context (`this`) 23 | 24 | [MDN Hacks](https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/) 25 | 26 | [MDN Arrow Functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) 27 | 28 | [SO `this` Keyword](https://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) 29 | 30 | #### ES6 In Depth - Jason Orendorff Series 31 | 32 | [ES6 In Depth](https://hacks.mozilla.org/author/jorendorffmozillacom/) 33 | 34 | ### Ruby/Rails 35 | 36 | [Toptal](https://www.toptal.com/ruby/interview-questions) 37 | 38 | [RoR Guides](https://guides.rubyonrails.org/) 39 | 40 | [Ruby Garage](https://rubygarage.org/blog/how-to-interview-your-ruby-on-rails-developer) 41 | --------------------------------------------------------------------------------