`. That means (remember!) that the `author_id` attribute will show up in your `params` nested under the `post` hash.
80 |
81 | The `:author_id` input to the `#select` helper above represents not just what the chosen value should be called (as in the `#select_tag`) but also which column name it represents in your original (in this case Post) model. This may be a bit annoying at first since you can't just name your choice whatever you want.
82 |
83 | If you have a `#form_for` form scoped under the `f` variable, you don't need to pass the `:post` symbol above (it gets it from `f`), so could instead use:
84 |
85 | ~~~ruby
86 | # app/views/posts/new.html.erb
87 | ...
88 | <%= f.select(:author_id, @user_options) %>
89 | ...
90 | ~~~
91 |
92 | It took a bit of time for us to get here, but hopefully you can now see how straightforward this method is for generating a potentially large dropdown.
93 |
94 | You'll use dropdowns a lot to populate the association of a model, e.g. the author of a Post. In the next section, we'll talk about how to actually create both model objects from within a single form.
95 |
96 | *Note: If you've used the `simple_form` gem, it has its own way of handling collections of objects which is a bit different (supposedly `simpler`) than this.*
97 |
98 | ### Nested Forms
99 |
100 | You've got a form for creating one of your User objects (say for your Amazon.com clone application) but you also want to make that form create one or more ShippingAddress objects (which a User can have many of). How do you get that one form to create both so your user doesn't get stuck clicking a bunch of form submits?
101 |
102 | This is a multi-part process. It involves your controller, view, models and routes... the whole MVC team! The gist of it is that your form will submit the main object (e.g. the User) as normal but it will sneak in a bunch of attributes for the other object(s) you want to create (e.g. ShippingAddress object(s)). Your model will have to be ready for this. It will create not just the original User object but also the nested objects at the same time.
103 |
104 | As you can imagine, it's important to get the names and parameters properly listed so all this magic can happen behind the scenes.
105 |
106 | We'll do a broad overview of the process here:
107 |
108 | 1. You will need to prepare the User model so that it knows to create one or more ShippingAddress objects if it receives their attributes when creating a normal User. This is done by adding a method to your User model called `#accepts_nested_attributes_for` which accepts the name of an association, e.g:
109 |
110 | ~~~ruby
111 | # app/models/user.rb
112 | class User < ActiveRecord::Base
113 | has_many :shipping_addresses
114 | accepts_nested_attributes_for :shipping_addresses
115 | end
116 | ~~~
117 |
118 | 2. Make sure you've allowed your `params` to include the nested attributes by appropriately including them in your Strong Parameters controller method. See the reading for examples of how to do this.
119 | 3. Build the form in the view. Use the `#fields_for` method to effectively create a `#form_for` inside your existing `#form_for` form.
120 |
121 | There are a couple new aspects to this process. You saw `#fields_for` in the [Basic Forms lesson](/courses/ruby-on-rails/lessons/form-basics) but it probably has new meaning to you now. It's basically how you create a form within a form (which should make sense since it's actually used behind the scenes by `#form_for`). In this example, we might create three "sub-forms" for ShippingAddress objects by using our association, e.g.
122 |
123 | ~~~ruby
124 | <%= form_for @user do |f| %>
125 | ...
126 | <% 3.times do %>
127 | <%= f.fields_for @user.shipping_address.build do |addy_form| %>
128 | ...
129 | <%= addy_form.text_field :zip_code %>
130 | ...
131 | <% end %>
132 | <% end %>
133 | <%= f.submit %>
134 | <% end %>
135 | ~~~
136 |
137 | Note that we could (and should) also have built the new shipping_address objects in the controller instead of the view; it's just for demonstration purposes here.
138 |
139 | The `#accepts_nested_attributes_for` method is fairly straightforward and the docs should be helpful.
140 |
141 | The reading will cover more about whitelisting the nested parameters.
142 |
143 | ### Deleting Nested Form Objects
144 |
145 | You can also have your form destroy nested forms by first setting the `:allow_destroy` option to `true` for the `#accepts_nested_attributes_for` method, e.g. `accepts_nested_attributes_for :shipping_addresses, :allow_destroy => true`. Now, any time you want to destroy a ShippingAddress object from a User's form, just include the key `_destroy => 1` in the submitted parameters for that ShippingAddress.
146 |
147 | ### Many-to-Many Relationships
148 |
149 | If you've got a `has_many :through` relationship, you'll likely need to go one additional step further by specifying that each side of your relationship is the inverse of the other. It's detailed in [this blog post from ThoughtBot](http://robots.thoughtbot.com/accepts-nested-attributes-for-with-has-many-through).
150 |
151 | ### Designing Your Own Forms
152 |
153 | Sometimes, despite all the nice helpers Rails gives you, you just want to do something that's not standard. You should first wonder whether this is the easiest and most straightforward way to do things. If it passes the smell test, then go ahead and build your form.
154 |
155 | It's often easiest (and good practice while you're learning) to start with the most basic of HTML forms. If you don't understand what's going on in the basic HTML (and remember to include your CSRF token), then you'll be hopeless trying to use helpers. Once you've got a good handle on things, gradually bring in the Rails helpers like `#form_tag` and `#form_for`.
156 |
157 | Don't get discouraged if you get some real head-scratcher moments when building nonstandard forms. It just takes some experience to feel comfortable. And if things are too out of hand, you may need to re-evaluate your approach (what exactly are you hoping to accomplish with your complex form?) and start again.
158 |
159 | ### Simple Form
160 |
161 | `simple_form` is a gem by Platformatec which can really make your life easier (if you aren't doing anything too crazy). It provides lots of user-friendly features for building forms and is in wide use today. We won't cover it explicitly here (though it's used in the tutorial we're following).
162 |
163 | It's up to you to check out [the documentation](https://github.com/plataformatec/simple_form) and start using it in your own applications as desired.
164 |
165 | ### Miscellania: Blank Submissions That Mean Delete
166 |
167 | Sometimes, for a record that already exists, you want to either deselect a dropdown or check none of your checkboxes but you want this to indicate that the associated fields should actually be set to `nil`. Usually, though, if you submit the form it will include none of the fields and your back end won't know that you actually wanted to remove those fields so nothing will happen. How do you get around it?
168 |
169 | Try making a hidden field in your form (or nested form) that has the same name as your checkboxes or dropdown but only contains the value `""`. Now you'll get that attribute to show up in your `params` hash no matter what and you can handle deleting the records however you'd like appropriate.
170 |
171 | Sometimes Rails helper methods will do it for you, but make sure you know what your form is actually submitting (if anything) if you deselect all options!
172 |
173 | ### Assignment
174 |
175 | 1. Read the [Rails Guide on Forms](http://guides.rubyonrails.org/form_helpers.html#option-tags-from-a-collection-of-arbitrary-objects) sections 3.3, which covers populating a form with a collection of objects.
176 | 2. Read the [Same Rails Guide on Forms](http://guides.rubyonrails.org/form_helpers.html#building-complex-forms) section 9, which covers accepting nested form data.
177 | 3. Read the [Same Rails Guide on Forms](http://guides.rubyonrails.org/form_helpers.html#understanding-parameter-naming-conventions) section 7, which covers the parameter conventions for nested forms.
178 | 4. Read [this blog post from Peter Rhoades](http://createdbypete.com/articles/working-with-nested-forms-and-a-many-to-many-association-in-rails-4/) on working with nested forms. The example covers a lot of the things we've gone over so far, so follow along. Also note how he does the whitelisting of nested attributes in Rails 4.
179 |
180 |
181 | ### Conclusion
182 |
183 | We've covered two of the more common use cases for complex forms -- pre-populating a form with objects and creating multiple objects with a single form. At this point, even if you're uncomfortable, you should have all the tools you need to work through creating a form. We'll get your hands dirty in the project, have no fear.
184 |
185 | The best part? This is more or less the most complicated conceptual stuff with learning Rails. Actually, it's not even really Rails-specific... once you're comfortable with the HTML that forms require and how the parameters get submitted to your controller, mapping that to the correct Rails conventions or helpers is the easy part. So everything you've learned may just be transferrable to every form you'll ever make.
186 |
187 | ### Additional Resources
188 | This section contains helpful links to other content. It isn't required, so consider it supplemental for if you need to dive deeper into something.
189 |
190 | * [Simple Form Documentation on Github](https://github.com/plataformatec/simple_form)
191 | * [`accepts_nested_attributes_for` documentation](http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html)
192 | * [Another example of a nested form on SO](http://stackoverflow.com/questions/15648396/rails-how-to-manage-nested-attributes-without-using-accepts-nested-attributes?rq=1)
193 | * [Using `inverse_of` to make `accepts_nested_attributes_for` work for `has_many :through` relationships](http://robots.thoughtbot.com/accepts-nested-attributes-for-with-has-many-through)
194 | * [Understanding Rails' form authenticity tokens](http://stackoverflow.com/questions/941594/understand-rails-authenticity-token)
195 | * [Why not to hardcode your application's secret token in production](http://daniel.fone.net.nz/blog/2013/05/20/a-better-way-to-manage-the-rails-secret-token/)
196 |
--------------------------------------------------------------------------------
/rails_programming/advanced_forms_and_activerecord/project_associations.md:
--------------------------------------------------------------------------------
1 | ### Introduction
2 |
3 | In these projects, you'll finally really get to dive into ActiveRecord's associations, one of the best features it offers. The tutorial chapter will have you creating microposts for your users and the other project will give you a chance to add some more interesting associations to your previous work.
4 |
5 | ### Warmup: Thinking Data First
6 |
7 | Lay out the data architecture you'd need to implement to build the following scenarios:
8 |
9 | 1. A site for pet-sitting (watching someone's pet while they're gone). People can babysit for multiple pets and pets can have multiple petsitters.
10 | 4. You like hosting people for dinner so you want to build a dinner party invitation site. A user can create parties, invite people to a party, and accept an invitation to someone else's party.
11 | 5. Extra credit (tricky): You and your friends just love posting things and following each other. How would you set up the models so a user can follow another user?
12 |
13 | ### Project 1: Ruby on Rails Tutorial
14 |
15 | This chapter of the tutorial begins the conceptual heavy lifting. For beginners trying to plough through the tutorial, this is usually the beginning of the "I'm doing it but not understanding it" phase. Luckily, you've already learned everything that will be covered in the tutorial and this should be a great chance to see it in action as part of a real website.
16 |
17 | The basic gist of it is that, to have a microposting site like Twitter, you've got to actually have microposts. Users create microposts, so you can surely expect that a User will `has_many` microposts. With the association done, the rest is really just about setting up the correct views to display the microposts.
18 |
19 | As with the previous chapter, just make sure you're writing the test specs in the correct files and stick with it. There's a lot of ground to cover but, again, the conceptual hurdles should be well within reach at this point.
20 |
21 | ### Your Task
22 |
23 |
24 | * Do the [Ruby on Rails Tutorial Chapter 13](https://www.railstutorial.org/book/user_microposts), "User microposts".
25 |
26 |
27 | ### Project 2: Private Events
28 |
29 | You want to build a site similar to a private [Eventbrite](http://www.eventbrite.com) which allows users to create events and then manage user signups. Users can create events and send invitations and parties (sound familiar?). Events take place at a specific date and at a location (which you can just store as a string, like "Andy's House").
30 |
31 | A user can create events. A user can attend many events. An event can be attended by many users. This will require you to model many-to-many relationships and also to be very conscious about your foreign keys and class names (hint: you won't be able to just rely on Rails' defaults like you have before).
32 |
33 | ### Your Task
34 |
35 | We've gotten quite far here, so these tasks will only lay out the high level overview of what you're meant to build. You'll need to implement the details yourself. As usual, it's not meant to look pretty, just work. Design is all extra credit.
36 |
37 |
38 | #### Setup and Sign In
39 |
40 | 1. Model the data for your application, including the necessary tables.
41 | 2. Create a new Rails application and Git repo called `private-events`.
42 | 3. Update your README to be descriptive and link to this project.
43 | 4. Build and migrate your User model. Don't worry about validations.
44 | 5. Create a simple Users controller and corresponding routes for `#new`, `#create`, and `#show` actions. You'll need to make a form where you can sign up a new user and a simple `#show` page. You should be getting better and faster at this type of vanilla controller/form/view building.
45 | 6. Create a simple sign in function that doesn't require a password -- just enter the ID or name of the user you'd like to "sign in" as and click Okay. You can then save the ID of the "signed in" user in either the `session` hash or the `cookies` hash and retrieve it when necessary. It may be helpful to always display the name of the "signed in" user at the top.
46 |
47 | #### Basic Events
48 |
49 | 1. Build and migrate your Event model without any foreign keys. Don't worry about validations. Include the event's date in your model but don't worry about doing anything special with it yet.
50 | 2. Add the association between the event creator (a User) and the event. Call this user the "creator". Add the foreign key to the Events model as necessary. You'll need to specify your association properties carefully (e.g. `:foreign_key`, `:class_name`, and `:source`).
51 | 3. Modify your User's Show page to list all the events a user has created.
52 | 4. Create an EventsController and corresponding routes to allow you to create an event (don't worry about editing or deleting events), show a single event, and list all events.
53 | 5. The form for creating an event should just contain a `:description` field.
54 | 6. The `#create` action should use the `#build` method of the association to create the new event with the user's ID prepopulated. You could easily also just use Event's `::new` method and manually enter the ID but... don't.
55 | 7. The event's Show page should just display the creator of the event for now.
56 | 8. Create the Event Index page to display all events.
57 |
58 | #### Event Attendance
59 |
60 | 1. Now add the association between the event attendee (also a User) and the event. Call this user the "attendee". Call the event the "attended_event". You'll again need to juggle specially named foreign keys and classes and sources.
61 | 2. Create and migrate all necessary tables and foreign keys. This will require a "through" table since an Event can have many Attendees and a single User (Attendee) can attend many Events... many-to-many.
62 | 3. Now make an Event's Show page display a list of attendees.
63 | 4. Make a User's Show page display a list of events they are attending.
64 | 5. Modify the User's Show page to separate those events which have occurred in the past ("Previously attended events") from those which are occuring in the future ("Upcoming events"). You could do this by putting logic in your view. Don't. Have your controller call separate model methods to retrieve each, e.g. `@upcoming_events = current_user.upcoming_events` and `@prev_events = current_user.previous_events`. You'll get some practice with working with dates as well as building some queries.
65 | 6. Modify the Event Index page to list all events, separated into Past and Upcoming categories. Use a class method on Event (e.g. `Event.past`).
66 | 7. Refactor the "upcoming" and "past" methods into simple scopes (remember scopes??).
67 | 8. Put navigation links across the top to help you jump around.
68 | 9. Extra Credit: Allow users to invite other users to events. Only allow invited users to attend an event.
69 | 10. Push to Github.
70 |
71 |
72 |
73 | ### Student Solutions
74 | Send us your solution so we can show others! Submit a link to the Github repo with your files in it here using any of the methods listed on the [contributing page](http://github.com/TheOdinProject/curriculum/blob/master/contributing.md). Please include your partner's github handle somewhere in the description if they would like attribution.
75 |
76 | * Add your solution below this line!
77 | * [Max Garber's solution](https://github.com/bubblebooy/private-events)
78 | * [Austin Fisher's solution](https://thawing-everglades-63943.herokuapp.com/)
79 | * [Javier Machin's solution](https://github.com/Javier-Machin/private-events) - [View in Browser](https://social-boost-app.herokuapp.com/)
80 | * [0zra's solution](https://github.com/0zra/event-planner)
81 | * [Raiko's solution](https://github.com/Cypher0/private-events) - [View in Browser](https://radiant-brook-28646.herokuapp.com/)
82 | * [Jmooree30's solution](https://github.com/jmooree30/private-events.git) - [View in Broswer](https://sleepy-wildwood-90425.herokuapp.com/)
83 | * [theghall's solution](https://github.com/theghall/odin-private-events.git)
84 | * [Clayton Sweeten's solution](https://github.com/cjsweeten101/private-events)
85 | * [Jonathan Yiv's solution](https://github.com/JonathanYiv/private-events) - [View in Browser](https://shielded-waters-45439.herokuapp.com)
86 | * [nmac's solution](https://github.com/nmacawile/Events)
87 | * [ToTenMilan's solution](https://github.com/ToTenMilan/the_odin_project/tree/master/rails/associations/private-events) - [View in browser](http://iventbrite.herokuapp.com/)
88 | * [mindovermiles262's solution](https://github.com/mindovermiles262/odin-events)
89 | * [holdercp's solution](https://github.com/holdercp/private-events) - [View in browser](https://aqueous-shore-53729.herokuapp.com/)
90 | * [jfonz412's solution](https://github.com/jfonz412/private-events)
91 | * [yilmazgunalp's solution](github.com/yilmazgunalp/private-events) - [Live version](https://my-private-events.herokuapp.com/)
92 | * [CloudTemplar_'s solution](https://github.com/cloudtemplar/private-events) - [Live version](https://shielded-falls-83468.herokuapp.com/)
93 | * [LPSV's solution](https://github.com/leosoaivan/TOP_ror_private_events)
94 | * [Jib's solution](https://github.com/NuclearMachine/odin_rails/tree/master/private_events) - [Live version](https://sheltered-crag-16269.herokuapp.com/)
95 | * [Austin's solution](https://github.com/CouchofTomato/private_events)
96 | * [Jamie's solution](https://github.com/Jberczel/private_events) - [View in browser](http://privent.herokuapp.com/)
97 | * [Donald's solution](https://github.com/donaldali/odin-rails/tree/master/private-events)
98 | * [TomTom's solution](https://github.com/tim5046/projectOdin/tree/master/Rails/private-events/private-events)
99 | * [Erithair's solution](https://github.com/N19270/private-events) - [View in browser](http://private-events.herokuapp.com/)
100 | * [Marina Sergeyeva's solution](https://github.com/imousterian/OdinProject/tree/master/Project3_EventBrite/private-events)
101 | * [Kate McFaul's solution](https://github.com/craftykate/odin-project/tree/master/Chapter_04-Advanced_Rails/private_events)
102 | * [Tommy Noe's solution](https://github.com/thomasjnoe/private-events)
103 | * [Vidul's solution](https://github.com/viparthasarathy/private-events)
104 | * [Aleksandar's solution](https://github.com/rodic/private-events)
105 | * [Vidul's solution](https://github.com/viparthasarathy/private-events) - [View in browser](https://fierce-headland-7201.herokuapp.com/)
106 | * [Jason Matthews' solution](https://github.com/fo0man/private-events)
107 | * [Dominik Stodolny's solution](https://github.com/dstodolny/private-events)
108 | * [Kevin Mulhern's solution](https://github.com/KevinMulhern/private_events) - [View in browser](https://privateevents.herokuapp.com/)
109 | * [AtActionPark's solution](https://github.com/AtActionPark/odin_private_event) - [View in browser](https://afternoon-citadel-2578.herokuapp.com/)
110 | * [Andrej Dragojevic's solution](https://github.com/antrix1/PrivateEvents) - [View in browser](https://calm-refuge-3455.herokuapp.com/)
111 | * [dchen71's solution](https://github.com/dchen71/private-events) - [View in browser](http://ronchon-moliere-6251.herokuapp.com/)
112 | * [Matias Pan's solution](https://github.com/kriox26/private-events) - [View in browser](https://polar-refuge-9964.herokuapp.com/)
113 | * [Tomislav Mikulin's solution](https://github.com/MrKindle85/private-events)
114 | * [Florian Mainguy's solution](https://github.com/florianmainguy/theodinproject/tree/master/rails/private-events)
115 | * [Radi Totev's solution](https://github.com/raditotev/private-events)
116 | * [cdouglass's solution](https://github.com/cdouglass/odin-project-exercises/tree/master/rails/private-events)
117 | * [Luke Walker's solution](https://github.com/ubershibs/rails_course/tree/master/private-events)
118 | * [Scott Bobbitt's solution](https://github.com/sco-bo/private-events)
119 | * [srashidi's solution](https://github.com/srashidi/Active_Record_Associations/tree/master/private-events)
120 | * [Max Gallant's solution](https://github.com/mcgalcode/private-events) - [View in browser](https://secret-gorge-95812.herokuapp.com/)
121 | * [Hassan Mahmoud's solution](https://github.com/HassanTC/private-events)
122 | * [Miguel Herrera's solution](https://github.com/migueloherrera/private-events)
123 | * [James Brooks's solution](https://github.com/jhbrooks/private-events) - [View in browser](https://polar-caverns-15299.herokuapp.com/)
124 | * [Matt Velez's solution](https://github.com/Timecrash/rails-projects/tree/master/private-events)
125 | * [Sander Schepens's solution](https://github.com/schepens83/theodinproject.com/tree/master/rails/project9--private-events/private-events)
126 | * [Mateo Mejia's solution](https://github.com/mateomgj/private-events) - [View in browser](https://fierce-brushlands-63385.herokuapp.com/)
127 | * [Fabricio Carrara's solution](https://github.com/fcarrara/private-events) - [View in browser](https://private-events-rails.herokuapp.com)
128 | * [Akshay Bharwani's solution](https://github.com/akshaybharwani/private-events)
129 | * [Martin Safer's solution](https://github.com/mtsafer/private-events) - [View in browser](https://aqueous-woodland-15720.herokuapp.com/)
130 | * [Shala Qweghen's solution](https://github.com/ShalaQweghen/private_events) - [View in browser](http://cryptic-mesa-97521.herokuapp.com/)
131 | * [John's solution](https://github.com/johnTheDudeMan/private_events)
132 | * [Oscar Y.'s solution](https://github.com/mysteryihs/private-events)
133 | * [Till S.'s solution](https://github.com/gitseti/private-events) - [View in browser](https://protected-citadel-60271.herokuapp.com/)
134 | * [Frank V's solution](https://github.com/fv42wid/events)
135 | * [Amrr Bakry's solution](https://github.com/Amrrbakry/rails_the_odin_project/tree/master/private_events) - [View in browser](https://fathomless-shore-10615.herokuapp.com/)
136 | * [David Chapman's solution](https://github.com/davidchappy/odin_training_projects/tree/master/private-events)
137 | * [Dylan's non RESTful solution](https://github.com/resputin/the_odin_project/tree/master/Rails/private-events)
138 | * [DV's solution](https://github.com/dvislearning/private-events) - [View in browser](https://sleepy-everglades-63021.herokuapp.com/)
139 | * [Sophia Wu's solution](https://github.com/SophiaLWu/private-events) - [View in browser](https://quiet-forest-50554.herokuapp.com/)
140 | * [Eric Gonzalez solution](https://github.com/Twinpair/Eventor) - [View in browser](https://rails-eventor.herokuapp.com/)
141 | * [Tom Westerhout's solution](https://github.com/TomWesterhout/private_events) - [View in browser](https://private-events-170388.herokuapp.com/)
142 | * [at0micred's solution](https://github.com/amyjording/private-events)
143 | * [Francisco Carlos's solution](https://github.com/fcarlosdev/the_odin_project/tree/master/private-events)
144 | * [Punnadittr's solution](https://github.com/punnadittr/private-events) - [View in browser](https://boiling-plains-19274.herokuapp.com/)
145 | * [Uy Bình's solution](https://github.com/uybinh/private_events_odin) - [View in browser](https://private-event-odin.herokuapp.com)
146 | * [Agon's solution](https://github.com/AgonIdrizi/private-events) - [View in browser](https://myprivateevents.herokuapp.com/)
147 |
148 | ### Additional Resources
149 | This section contains helpful links to other content. It isn't required, so consider it supplemental for if you need to dive deeper into something.
150 |
151 | * [ActiveRecord Querying](http://guides.rubyonrails.org/active_record_querying.html)
152 |
--------------------------------------------------------------------------------
/rails_programming/advanced_forms_and_activerecord/project_associations_2.md:
--------------------------------------------------------------------------------
1 | ### Introduction
2 |
3 | This final chapter of the RoR Tutorial will require you to have users following users.
4 |
5 | ### Project: Ruby on Rails Tutorial
6 |
7 | You're on the last chapter! This one will require you to implement the final piece of functionality that makes microblogging what it is -- allowing users to follow each other. With your now excellent knowledge of associations, you should be able to see that you'll run into some naming issues (you won't get too far with a User who `has_many :users`). It's a many-to-many relationship that uses the SAME model for both sides ("self-referential")!
8 |
9 | As an added bonus, you'll get a chance to touch a bit of AJAX when making it so the buttons for following don't actually refresh the page with each push. Hooray learning!
10 |
11 | ### Assignment
12 |
13 |
14 | 1. You've got a User model. Write down how you would implement (and name) the associations necessary to make one user follow another. You'll get a lot more out of the tutorial chapter if you think it through first.
15 | 2. Do the [Ruby on Rails Tutorial Chapter 14](https://www.railstutorial.org/book/following_users), "Following users".
16 |
17 |
18 | ### Additional Resources
19 | This section contains helpful links to other content. It isn't required, so consider it supplemental for if you need to dive deeper into something.
20 |
21 | * [Railscast on Many-to-Many Self-Referential Relationships (2010)](http://railscasts.com/episodes/163-self-referential-association?view=asciicast). This is old but should be helpful to see the concept covered.
22 |
--------------------------------------------------------------------------------
/rails_programming/advanced_forms_and_activerecord/project_forms_advanced.md:
--------------------------------------------------------------------------------
1 | ### Project: Flight Booker
2 |
3 | In this project, you'll get a chance to tackle some more advanced forms. This is the kind of thing you'll have to work with when handling user orders for anything more complicated than an e-book. In this case, we'll build the first three steps of a typical checkout process for booking a one-way flight:
4 |
5 | A typical airline booking flow:
6 |
7 | 1. Enter desired dates / airports and click "Search"
8 | 2. Choose from among a list of eligible flights
9 | 3. Enter passenger information for all passengers
10 | 4. Enter billing information
11 |
12 | Step 4 would be done via integration of something like [Paypal](http://coding.smashingmagazine.com/2011/09/05/getting-started-with-the-paypal-api/), via [a gem](https://github.com/nov/paypal-express) or [an SDK](http://www.tommyblue.it/2013/07/03/paypal-express-checkout-with-ruby-on-rails-and-paypal-sdk-merchant) or [Stripe](https://stripe.com/docs/checkout/guides/rails).
13 |
14 | ### Assignment
15 |
16 | We'll be creating a one-way flight booker. You'll get lots of practice populating and building dropdown menus, radio buttons, and nested submissions. Let the fun begin!
17 |
18 | This project will require you to seed your database, so use your `db/seeds.rb` file to write the code necessary to populate Airports and Flights. One trick for toy apps like this (don't do it for production!) is to make your seeds file `::delete_all` items from each table in your database and then completely repopulate them. That way, when you create a new model or change how you want your sample data set up, you can just update the `seeds.rb` file and rerun `$ rake db:seed`.
19 |
20 |
21 | #### Setup
22 |
23 | 1. Think through the data architecture required to bring this to life.
24 | 2. Create a new Rails app, e.g. `odin-flight-booker`, and a new Git repo
25 | 3. Modify the README to explain what you're doing and link back to the project here.
26 |
27 | #### Screen 1: Search
28 |
29 | For the first screen, you'll need a dropdown that lists a possible "From" airport and a possible "To" airport. just assume all flights fly between SFO and NYC. You'll need to set up a dropdown menu that contains all eligible flights.
30 |
31 | This requires relatively vanilla forms that happen to be prepopulated with collections of data. Working with dates will cover a bit of new ground for you.
32 |
33 | 1. Create an Airport model (which basically just needs an airport code like "SFO" or "NYC") and use the `db/seeds.rb` file to create several of them.
34 | 2. Create a Flight model (with the start and finish airport `id`s, start datetime, and flight duration).
35 | 3. Set up associations so you can ask `Flight.first.from_airport`, `Flight.first.to_airport` and get back Airport objects. Same for `Airport.first.departing_flights` and `Airport.first.arriving_flights`, which should return a list of flight objects.
36 | 4. Seed your database with flights.
37 | 5. You will search and view results in the same page. Set up your FlightsController and routes to make the Index page (`/flights`) the root route.
38 | 6. Create your search form on the `/flights` index page to submit using a GET (not POST) request back to the same URL.
39 | 7. The home page should contain four dropdown menus -- a list of FROM and TO airports, number of passengers (1-4), and a DATE dropdown for the flight date. The DATE dropdown should only include dates that have existing flights. Don't worry about restricting the contents of the FROM/TO dropdowns -- you'd normally use Javascript -- so just allow the user to select any airport. See [this SO post on formatting date in dropdown lists](http://stackoverflow.com/questions/15720940/rails-format-date-in-drop-down-list-in-view), [this quickie SO post on selecting months/years](http://stackoverflow.com/questions/13001904/rails-drop-down-select-month-year), and the Rails [DateHelper API Docs](http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html) for some help with creating Date selects.
40 |
41 | #### Screen 2: Pick a Flight
42 |
43 | Once search results are returned, the user just needs to choose from among the eligible flights.
44 |
45 | 1. Once the user has submitted the form, your controller should detect the additional query parameters (e.g. from `/flights?from_code=SFO&to_code=NYC&date=20131215&num_tickets=2`) and should pull the flights which match that criteria from your database through smart querying model methods.
46 | 2. Send those flights back to your `app/views/flights/index.html.erb` view. Make it so that the view will display the results below (if present).
47 | 3. Don't get rid of the search dropdowns -- keep the search form active at the top so the user can try running a new search.
48 | 4. The search results will need to be in their own form -- this time for selecting which of the flights the user would like to choose. Each returned flight should render with a radio button next to it. The user will select which flight to submit and move onto the next step. The form should submit to the `#new` action of the BookingsController you're about to create using another GET method. You'll need to include a hidden field that contains the number of passengers.
49 |
50 | #### Screen 3: Passenger Information
51 |
52 | Once the user has submitted their chosen flight, it's time to take their booking information. You'll need a form to submit the Booking and also create a Passenger object for each passenger who needs a ticket. Don't worry about creating a separate "Ticket" object, we'll assume the airline will issue those once the booking is verified.
53 |
54 | 1. Create and migrate the Booking model
55 | 2. Create and migrate the Passenger model (just keep the information simple -- name and email only)
56 | 3. Set up associations between Bookings, Passengers, and Flights.
57 | 4. Create a BookingsController and appropriate routes.
58 | 5. Set up your `#new` action, which should have received the flight ID and passenger number parameters, and use it to help render a form for a new booking which displays the currently chosen date, airports, flight ID and a set of fields to enter personal information for each passenger. You'll want to create a new blank Passenger object in your controller for each passenger, and then use `#fields_for` in the view to set up the sub-forms.
59 | 6. Try submitting the form and check out the parameters in your server.
60 | 7. You'll need your `#create` action to create a new Booking (linking it to the appropriate Flight) but it will also need to accept the nested attributes for each of the Passenger objects and create a new Passenger from them. Be mindful of whitelisting the nested parameters as well.
61 | 8. Go to the Booking model and implement the `#accepts_nested_attributes_for` method. See the [Rails API](http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html) for examples and [this SO post](http://stackoverflow.com/questions/18540679/rails-4-accepts-nested-attributes-for-and-mass-assignment) for another.
62 | 9. Once your form is successfully submitted, render the booking's `#show` page which displays the booking information (flight and passenger information).
63 | 10. Make sure your ticketing flow is working properly. Good work!
64 |
65 |
66 |
67 | ### Student Solutions
68 | Send us your solution so we can show others! Submit a link to the Github repo with your files in it here using any of the methods listed on the [contributing page](http://github.com/TheOdinProject/curriculum/blob/master/contributing.md). Please include your partner's github handle somewhere in the description if they would like attribution.
69 |
70 | * Add your solution below this line!
71 | * [Max Garber's solution](https://github.com/bubblebooy/odin-flight-booker)
72 | * [Javier Machin's solution](https://github.com/Javier-Machin/flight-booker) - [Live](https://easy-flight.herokuapp.com/)
73 | * [nmac's solution](https://github.com/nmacawile/fly-me-there) - [Heroku](http://protected-castle-72511.herokuapp.com)
74 | * [brxck's solution](https://github.com/brxck/flight-booker) - [View in Browser](https://nameless-sierra-62923.herokuapp.com/)
75 | * [Jmooree30's solution](https://github.com/jmooree30/flight-booker.git) - [View in Browser](https://powerful-wildwood-98145.herokuapp.com/)
76 | * [theghall's solution](https://github.com/theghall/odin-flight-booker.git)
77 | * [Clayton Sweeten's solution](https://github.com/cjsweeten101/odin-flight-booker)
78 | * [Jonathan Yiv's solution](https://github.com/JonathanYiv/flight-booker) - [View in Browser](https://fast-gorge-36383.herokuapp.com)
79 | * [mindovermiles262's solution](https://github.com/mindovermiles262/flight-booker) - [View in Browser](https://flight-booker-ad.herokuapp.com/)
80 | * [leosoaivan's solution](https://github.com/leosoaivan/TOP_ror_flight_tracker) - [View in Browser](https://immense-lowlands-59487.herokuapp.com/)
81 | * [yilmazgunalp`s solution](https://github.com/yilmazgunalp/flight_booker) - [View in Browser](https://sheltered-lowlands-87484.herokuapp.com/)
82 | * [Joshua Wootonn`s solution](https://github.com/jose56wonton/flight-booker) - [View in Browser](https://flight-booker-jw.herokuapp.com/)
83 | * [holdercp's solution](https://github.com/holdercp/odin-flight-booker) - [Live site](https://flight-booker-odin.herokuapp.com/)
84 | * [Jfonz412's solution](https://github.com/jfonz412/flight-booker)
85 | * [Orlando's solution](https://github.com/orlandodan14/Ruby-on-Rails/tree/master/Flight_booker)
86 | * [CloudTemplar_'s solution](https://github.com/cloudtemplar/flight-booker) - [Live on Heroku!](https://protected-escarpment-37952.herokuapp.com/)
87 | * [Jib's solution](https://github.com/NuclearMachine/odin_rails/tree/master/odin-flight-booker) - [Live website](https://sleepy-oasis-66283.herokuapp.com)
88 | * [Sic's solution](https://github.com/sic-f/flight-booker) - [View in browser](http://flyt-booker.herokuapp.com)
89 | * [Austin's solution](https://github.com/CouchofTomato/flight-booker)
90 | * [spierer's solution](https://github.com/spierer/odin-advanced-forms)
91 | * [jamie's solution](https://github.com/Jberczel/Flight_Booker) - [View in browser](http://flight-booker.herokuapp.com)
92 | * [Donald's solution](https://github.com/donaldali/odin-flight-booker) - [View in browser](http://dna-flight-booker.herokuapp.com/)
93 | * [Adrian Badarau's solution](https://github.com/adrianbadarau/flight-booking-app) - [View in browser](http://flight-booking-app.herokuapp.com/)
94 | * [Marina Sergeyeva's solution](https://github.com/imousterian/FlightBooker) - [View in browser](https://one-way-ticket.herokuapp.com/)
95 | * [Kate McFaul's solution](https://github.com/craftykate/odin-project/tree/master/Chapter_04-Advanced_Rails/flight-booker)
96 | * [Vidul's solution](https://github.com/viparthasarathy/odin-flight-booker) - [View in browser](https://intense-retreat-7685.herokuapp.com/)
97 | * [Dominik Stodolny's solution](https://github.com/dstodolny/odin-flight-booker)
98 | * [Alex's solution](https://github.com/alexgh123/odin-flight-booker)
99 | * [Kevin Mulhern's solution](https://github.com/KevinMulhern/flight_booker) - [view in browser](https://odin-booker.herokuapp.com/)
100 | * [AtActionPark's solution](https://github.com/AtActionPark/odin_flight_booker)
101 | * [Andrej Dragojevic's solution](https://github.com/antrix1/flight-booker) - [View in browser](https://blooming-mountain-4761.herokuapp.com/)
102 | * [dchen71's solution](https://github.com/dchen71/odin-flight-booker) - [View in browser](http://true-syrup-4655.herokuapp.com/)
103 | * [Matias Pan's solution](https://github.com/kriox26/flight-booker) - [View in browser](https://vast-ridge-2462.herokuapp.com/)
104 | * [Florian Mainguy's solution](https://github.com/florianmainguy/theodinproject/tree/master/rails/flight-booker)
105 | * [Tomislav Mikulin's solution](https://github.com/MrKindle85/tom-flight-booker)
106 | * [Hassan Mahmoud's solution](https://github.com/HassanTC/Flight-Booker)
107 | * [Radi Totev's solution](https://github.com/raditotev/flight_booker)
108 | * [Patrick Mallee's solution](https://github.com/patmallee/flight-booker)
109 | * [cdouglass's solution](https://github.com/cdouglass/odin-project-exercises/tree/master/rails/flight-booker)
110 | * [Luke Walker's solution](https://github.com/ubershibs/rails_course/tree/master/odin-flight-booker)
111 | * [srashidi's solution](https://github.com/srashidi/Advanced_Forms/tree/master/odin-flight-booker)
112 | * [Scott Bobbitt's solution](https://github.com/sco-bo/flight-booker) - [View in Browser](https://radiant-fortress-17197.herokuapp.com/)
113 | * [James Brooks's solution](https://github.com/jhbrooks/flight-booker) - [View in browser](https://rocky-earth-72202.herokuapp.com/)
114 | * [Miguel Herrera's solution](https://github.com/migueloherrera/odin-flight-booker)
115 | * [Matt Velez's solution](https://github.com/Timecrash/rails-projects/tree/master/flight-booker)
116 | * [Sander Schepens's solution](https://github.com/schepens83/theodinproject.com/tree/master/rails/project10--odin-flight-booker/odin-flight-booker)
117 | * [Stefan (Cyprium)'s solution](https://github.com/dev-cyprium/odin-flight-booker) - [View in browser](https://flight-booker-stefan.herokuapp.com/)
118 | * [Fabricio Carrara's solution](https://github.com/fcarrara/flight-booker) - [View in browser](https://flight-booker-rails.herokuapp.com)
119 | * [Shala Qweghen's solution](https://github.com/ShalaQweghen/odin_booker) - [View in browser](https://guarded-atoll-24378.herokuapp.com/)
120 | * [Frank V's solution](https://github.com/fv42wid/flight)
121 | * [chrisnorwood's solution](https://github.com/chrisnorwood/odin-flight-booker) - [View in browser](http://top-flight-booker.herokuapp.com/)
122 | * [Oscar Y.'s solution](https://github.com/mysteryihs/odin-flight-booker)
123 | * [Amrr Bakry's solution](https://github.com/Amrrbakry/rails_the_odin_project/tree/master/odin_flight_booker) - [View in browser](https://shrouded-earth-72460.herokuapp.com/)
124 | * [Cody Loyd's Solution](https://github.com/codyloyd/flight_booker) - [View in Browser](https://intense-peak-10624.herokuapp.com/)
125 | * [Rhys B.'s Solution](https://github.com/105ron/flight_booker) - [View in Browser](https://secure-depths-25814.herokuapp.com)
126 | * [David Chapman's Solution](https://github.com/davidchappy/flight_booker)
127 | * [Dylan's Solution](https://github.com/resputin/the_odin_project/tree/master/Rails/odin-flight-booker)
128 | * [Marcus' solution](https://github.com/nestcx/odin-flight-booker)
129 | * [DV's Solution](https://github.com/dvislearning/odin-flight-booker) - [View in Browser](https://powerful-sierra-48857.herokuapp.com/)
130 | * [Peter Mester's solution](https://github.com/peter665/FlightBooker) - [View in Browser](http://flight-booker-mester.herokuapp.com/)
131 | * [Francisco Carlos's solution](https://github.com/fcarlosdev/the_odin_project/tree/master/odin-flight-booker)
132 | * [Punnadittr's solution](https://github.com/punnadittr/flight-booker) - [View in Browser](https://frozen-everglades-39378.herokuapp.com/)
133 | * [Areeba's solution](https://github.com/AREEBAISHTIAQ/flight-booker)
134 | * [Agon's solution](https://github.com/AgonIdrizi/flight-booker) - [View in Browser](https://flight-booker-agon.herokuapp.com/)
135 |
136 | ### Additional Resources
137 | This section contains helpful links to other content. It isn't required, so consider it supplemental for if you need to dive deeper into something.
138 |
139 | * [Rails API for nested attributes](http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html)
140 |
--------------------------------------------------------------------------------
/rails_programming/apis_mailers_advanced_topics/api_interfacing.md:
--------------------------------------------------------------------------------
1 | ### Introduction
2 |
3 | It's pretty easy to turn your application into an API, but what about grabbing data from other APIs across the internet? That's where things usually get a bit more challenging because they're all different and you've got to authenticate yourself.
4 |
5 | Most popular services offer APIs so developers can interface with them (they love it if you can get more people using their platform). Facebook, Twitter, Instagram, Flickr, Dropbox, AirBnB... They've all got APIs. Just Google "companyX API docs" and you'll get to their developers section.
6 |
7 | If you go to the documentation for an API, it can sometimes be a bit overwhelming because much of the material sort of assumes that you know what you're doing, but some are definitely better than others. There are also elements that are common across almost all of them. The more you work with APIs (and get the hang of the authentication flow), the easier they get. You'll be making mashups in no time.
8 |
9 | This lesson will cover some general steps that are common across APIs and will do high level overviews of some of the methods for authenticating with APIs like Omniauth. Try to gain as much conceptual understanding as you can here and use the documentation each gem or API provides to help with the implementation. If you find great free learning resources that explain APIs, please let us know [(make a pull request and add an additional resource at the bottom of this page)](https://github.com/TheOdinProject/curriculum)!
10 |
11 | ### Learning Outcomes
12 | Look through these now and then use them to test yourself after doing the assignment.
13 |
14 | * What's the best way to locate an API's docs?
15 | * What are the steps you'll almost always have to go through when setting up your app to use an API?
16 | * What is an API key?
17 | * How is API use typically controlled?
18 | * How do you avoid including an API's secret token in your Github repo (e.g. hard coding it)?
19 | * Why is it important to know which API version you're using?
20 | * What is a RESTful API and why does that make your life easier?
21 | * What (basically) is OAuth?
22 | * Why would a user prefer to sign into your site using Facebook instead of giving you a new password?
23 | * How (basically) does this process work from the user's perspective?
24 | * How (basically) does this process work from your (the application developer's) perspective?
25 | * What are the basic rules of thumb for not abusing APIs?
26 | * What is OmniAuth and why does it save you tons of time/pain?
27 | * What is an SDK and why are they useful for working with APIs?
28 |
29 | ### First Steps
30 |
31 | You will need to register yourself and your application with the API provider. They will track and monitor your usage this way. Typically, there is a free tier of usage and it will cost you if you're going over your limits, whether they are expressed in terms of total MB downloaded, number of requests, or number of transactions (for instance). Usually, even if you're a user of their service, you will need to separately go to their developer portal.
32 |
33 | You will need to get an **API key** from the API provider, which often is the last part of registration. This key will uniquely identify your application to the API provider and you will send it along with every request. It's sort of like a user on your site automatically submitting their session cookie with each request so you know they're logged in.
34 |
35 | You'll typically also get a "secret key" or similarly named code. Whereas the API key is usually public, the secret key is an added layer of security that's required for the more secure APIs and you will use it to generate a cryptographic token that authenticates any requests you make. You will need to make sure you don't include this secret key in your Git repo! Use the `figaro` gem or environment variables to put the key on the server instead of hard coding it.
36 |
37 | ### API Rates and Security Tokens
38 |
39 | Most APIs require a different type of "security clearance" for different requests:
40 |
41 | 1. You can usually make innocuous requests like asking for Tweets (with Twitter's API of course) with straightforward and unauthenticated GET requests. You can make these from any command line or a browser tool like [Chrome's Rest Client](http://getpostman.com). These types of API requests are usually severely rate limited to incentivize you to sign up.
42 | 2. The next layer is making requests that include your API key. These are still usually fairly innocuous things (like getting public data) and limited by the API's pricing tiers.
43 | 3. More sensitive requests like asking for specific user data or submitting/modifying/deleting data will likely require you to use an authentication process involving your secret token. We'll cover the basics of this in the project. Rates for these requests are subject to the API pricing tiers.
44 | 4. Oftentimes, you actually want to make requests on behalf of a user. For instance, showing a user a dashboard of all his tweets and Facebook posts together would require asking Twitter for that user's tweets and Facebook for that user's posts. This can involve a LOT of requests over a large user base, but luckily you're actually able to make them on the user's behalf by asking for the user's permission. We'll cover this as well later, but basically you send the user to the API provider's site to sign in, then the API provider will give you a user-specific token to use when making requests on their behalf in the future. Rates for this are usually more advantageous because they are covered in a per-user bucket. We typically use the OAuth protocol for this, as described below.
45 |
46 | ### Versions
47 |
48 | Tech companies, especially if they've been around for a while, typically have several versions of their API. Usually, they built a first draft version when they were a startup and then had to build a real one later, but still have to support the original since so many other applications are likely to be using it.
49 |
50 | Just go for the latest version unless you have a compelling reason not to (it's usually got a separate section of documentation for each version). Pay attention to which version's docs you're looking at... there's nothing like the frustration of realizing you've been looking at the v1 API instead of the v2 API docs.
51 |
52 | ### RESTful APIs
53 |
54 | Just like your routes should ideally be set up in a RESTful way, so too should APIs. These days, most APIs are RESTful, meaning the usual batch of GET/PUT/PATCH/DELETE requests will retrieve resources as expected. The good part about setting up your application routes RESTfully is that your API will be the same way.
55 |
56 | Working with RESTful APIs at least takes a lot of the headache out of the process since you can usually sort of guess what you should be doing, then can head over to the docs to figure out how exactly and what format you'll get the results back in. For instance, just like you'd expect the GET request to the `/users` route in your app to display a page listing all its users, a GET request to a RESTful API for the same route should probably give you back a JSON or XML object filled with all the users (or at least some paginated sample of them).
57 |
58 | ### Oauth and Login via API
59 |
60 | You've seen sites where their homepage lets you log in using Facebook or Twitter or LinkedIn. After logging in with one of those third-party services you magically have a new account with them but don't need to give them a new password.
61 |
62 | OAuth is usually behind this. It solves the very real problem of user data privacy and access control. The idea is that the user shouldn't need to give your awesome application their password in order for you to make requests on their behalf. So OAuth allows Facebook to act as a sort of intermediary when dealing with the user.
63 |
64 | This has two great use cases -- logging in the user with an external site like Facebook and making requests for or submissions of data on the User's behalf.
65 |
66 | In the first case, you never actually discover the user's Facebook password. They get redirected to Facebook's website to sign in, where they authorize you to use some of their data (which you'll have to configure when you set up this process). Facebook then calls you back (by sending a request to a URL of your choosing with the proper data) and gives you a special access token for that user. You can then use that to get any additional data that the user has authorized you to access (like their name and email address). Now you can create a new user in your database with that data and everyone's relatively happy.
67 |
68 | From the user's perspective, they only need to remember their Facebook password to log into your site so it's easy for them. They also don't need to trust you with their password. As long as they trust Facebook, they can use your site. Also, if your site is doing bad things with their data (like when SocialCam spammed the hell out of everyone's Facebook timeline for a while), they can tell Facebook to stop allowing you to use their data and Facebook will turn off access for the token they gave you.
69 |
70 | From Facebook's perspective, they retain majority ownership of the user and nestle deeper into everyone's lives (for better or for worse).
71 |
72 | Once you have the user token, you are allowed to make requests or submissions based on whatever permissions Facebook (and the user) authorized you to. That could involve asking who their friends are, posting on their timeline, or "liking" something. You just need to include the token as a part of your authentication process for the request.
73 |
74 | #### Restrictions
75 |
76 | Every site, in addition to rate limiting their API, will have terms of use. This usually covers what you're allowed to do with the data you gather. For instance, they probably aren't going to allow you to store a whole bunch of their data in your own database (harvesting). They also probably won't allow you to do anything SPAMmy or which will hurt their user's experience. Common sense should be your initial guide, but consult the TOU documentation for specifics. Basically, if you'll be hurting their user's experience or taking their precious data, you can't do it.
77 |
78 | It should be noted that any time you develop using someone else's API, you're putting yourself at their mercy. If you're using it to the point where you pay for data, they'll love you. If you get too successful and become competitive with them, they've got you by the neck and will cut you off or demand exorbitant fees. Just be cognizant of those risks.
79 |
80 | #### OAuth 2.0 Basics
81 |
82 | OAuth 2.0 is actually pretty complicated, so I'll just cover the basic process. You don't really need to care how exactly it works behind the scenes until you start really developing with it or trying to create your own API that uses it. You **will** need to understand the steps involved in the user experience because, well, the user is the reason you're building your application.
83 |
84 | Basically (still using Facebook as an example):
85 |
86 | 1. User tries to access a page on your app and you ask the user to login
87 | 2. User chooses the "Login With Facebook" option
88 | 3. User is redirected to a Facebook page asking them to review the permissions you are asking for and telling them to sign in. The URI will contain parameters that tell Facebook who your application is and possibly which URI they should submit their response to (or maybe you specified this as a part of your API registration process with them).
89 | 4. User decides you seem like a fun application so he'll allow you to see his email address and post to his timeline. He signs in to his Facebook account. Facebook creates an authorization code and sends it back to your application's callback URI.
90 | 5. The user waits while your application takes that authorization code and uses it to ask Facebook for the real good stuff. Facebook makes sure your application is the same one the user authorized, then POSTs back to you a unique authentication token for the user (which likely expires in 90 days) and any data you asked for up front (like email address).
91 | 5. You store the user's unique token in your database and use it, along with your application key(s), to make any subsequent requests on the user's behalf.
92 |
93 | See [this brief overview of OAuth 2.0](http://tutorials.jenkov.com/oauth2/overview.html) for an overview. Then check out [this more substantive explanation from tutsplus.com](http://net.tutsplus.com/tutorials/oauth-2-0-the-good-the-bad-the-ugly/).
94 |
95 | #### Implementing OAuth 2.0 in Rails -- Use Omniauth!
96 |
97 | This sounds horribly complicated! Someone must have made a gem for it...
98 |
99 | Luckily someone has. Many someones, actually. There is a generic OAuth gem called `omniauth` (docs available [on Github](https://github.com/intridea/omniauth)) and then a separate gem which provides a specific authentication strategy for every major API (see the list [HERE](https://github.com/intridea/omniauth/wiki/List-of-Strategies)). Once you've gone through things with one of them, you'll become comfortable with all of them.
100 |
101 | I'll leave it to the projects to walk through using Omniauth since it's much easier to learn by doing than reading a bunch of bullet points.
102 |
103 | ### SDKs
104 |
105 | In addition to or instead of API access, many companies provide SDKs (software development kits). Usually these are Javascript libraries that contain all the code necessary to access their API. This can be useful because you're then able to access the API with simple Javascript methods instead of doing backflips on your own backend. It comes with the downside, however, of expanding your code base and forcing you to use their conventions for everything.
106 |
107 | We won't cover SDKs explicitly in this course but they should be well within reach to pick up by reading the documentation.
108 |
109 | ### Assignment
110 |
111 |
112 | 1. Watch [this Railscast on using Omniauth to allow Twitter Signin](http://railscasts.com/episodes/241-simple-omniauth-revised).
113 | 2. Read through the [Omniauth documentation](https://github.com/intridea/omniauth)
114 | 3. Pick an API that you really might like to interface with or a web product you use almost every day (e.g. Google, Facebook, Instagram...). Google for its docs, e.g. with "instagram api documentation", and have a look at them. Some docs are better than others, but they will be your source for understanding which methods you can call, what they will return, how to register your application to get an API key, and more useful tidbits.
115 |
116 |
117 | ### Conclusion
118 |
119 | APIs are fundamental to making rich web applications and they're also a lot of fun -- it makes your app feel a lot more "real" when you're able to let your users log in using Facebook or if you can display information from different sources across the web. If you're building a startup, it improves the user experience enough that you'll likely see an uptick in conversion rates.
120 |
121 | Working with external APIs can be incredibly rewarding because you're able to leverage functionality that others have spent a lot of time perfecting but also incredibly frustrating because they're all different and you have to rely on gems which are doing things behind the scenes that you're not entirely sure of. Over time, you'll start to see a lot of similarities between APIs and the procedure for working with them will feel more and more comfortable. To help get you there, the next project will have you working with an API and your final project will have you implementing signin via API as well.
122 |
123 | ### Additional Resources
124 | This section contains helpful links to other content. It isn't required, so consider it supplemental for if you need to dive deeper into something.
125 |
126 | * See [This SO Post on interfacing with third-party APIs](http://stackoverflow.com/questions/6228870/interfacing-with-a-third-party-api-in-rails-opeing-urls-and-parsing-xml-json) for tips.
127 | * [RailsConf 2016 - From Zero to API Hero: Consuming APIs like a Pro by Cecy Correa](https://www.youtube.com/watch?v=Af5HDgvGuXk)
128 |
--------------------------------------------------------------------------------
/rails_programming/apis_mailers_advanced_topics/conclusion.md:
--------------------------------------------------------------------------------
1 | You've reached the end of the scope of this course and should feel pretty good about your Rails skills. That doesn't mean you should feel like you understand everything, but you should be able to build a Rails application with the kind of functionality which is actually useful in the real world. It probably won't look too pretty, but that will come in the next few courses.
2 |
3 | This isn't the last course in the curriculum and you've got quite a way to go before hitting that "full stack" point, but it's as far as we'll get with Rails and it's pretty darn far. Despite how far you've come, it's up to you to keep building stuff, asking questions, and digging deeper into both Rails and best practices of software engineering in general.
4 |
5 | At this point, you've got the Rails building blocks but the learning is never finished. Your first six months on the job as a Rails dev will involve learning at least as much information as you just did. The point of this course has been to get you to a place where you can start thinking about actually getting that first job.
6 |
7 | ### Next Steps
8 |
9 | This final lesson isn't designed to explicitly teach new material, just point you in the right direction if you'd like to dig a bit deeper before moving on. Or maybe you'll bookmark it and come back when you want to see where to head next in your Rails journey.
10 |
11 | Rails is deep... there's a lot going on behind the scenes and it will take a long time to actually understand how it all works and the best ways to use it. The best way to figure it out is to work for a company that uses it and get paid to learn. If that's not an option, building your own stuff and reading on your own time can be a good substitute. There's new information being created every day in blog posts, Stack Overflow posts, and podcasts.
12 |
13 | A good first step is to reread the [Rails Guides](http://guides.rubyonrails.org/index.html). There are some sections we skipped and some guides we never got to (especially in the "digging deeper" section). In particular, the following sections might be interesting:
14 |
15 | #### Security
16 |
17 | If you start working on more public-facing applications, security will become more important. Check out the [Rails Guide on Security](http://guides.rubyonrails.org/security.html) for more information.
18 |
19 | #### Rake Tasks
20 |
21 | Rake tasks are a special way of running scripts that can use your application. You're already familiar with some like `$ rake db:migrate`, but you can also create your own. This website uses a Rake task to populate the curriculum from its Github repo. Check out the [Rails Guide on Command Line and Rake Tasks](http://guides.rubyonrails.org/command_line.html) for more information.
22 |
23 | #### Caching
24 |
25 | Caching makes your application faster by reducing database calls. Check out the [Rails Guide on Caching](http://guides.rubyonrails.org/caching_with_rails.html) to learn more.
26 |
27 | ### Books
28 |
29 | Here are a few books that will take you a bit deeper as well:
30 |
31 | * [The Rails 4 Way](https://leanpub.com/tr4w) by Obie Fernandez is a comprehensive guide to the framework.
32 | * [Agile Web Development with Rails 5](https://pragprog.com/book/rails5/agile-web-development-with-rails-5) is sort of an in-depth guide combined with a tutorial that follows Agile practices along the way.
33 | * [Rails Antipatterns](http://www.amazon.com/Rails-AntiPatterns-Refactoring-Addison-Wesley-Professional/dp/0321604814) is a good best-practices guide.
34 |
35 | ### Other Resources
36 |
37 | * [RailsCasts by Ryan Bates](http://railscasts.com/) are incredibly valuable. Great ways to actually see the implementation of various gems or practices.
38 | * [GoRails](https://gorails.com/) alternative to RailsCasts.
39 | * The [RailsApps Project](https://tutorials.railsapps.org/) provides lots of great tutorials that walk you through building real-life scenarios like integrating Devise and CanCan with your app or building a payments backend.
40 | * [RubyFlow community blog](http://www.rubyflow.com/) will have some interesting stuff pop up from time to time.
41 |
42 | ### Contributing
43 |
44 | You know more than you think. Remember when we just said that you should keep building stuff? This website is completely open source and needs your help to improve. We have a dedicated group of current and former students who help add features and proofread the curriculum. The best part is that it's completely public and free so you can watch or participate at whatever level you're comfortable.
45 |
46 | It's a great way to start learning about agile development methodologies and to start doing meaningful development work in a friendly and supportive environment. So check out the contributing channel on our [gitter](https://gitter.im/TheOdinProject/theodinproject) to see what we're up to. We'd love to have you get involved!
47 |
48 | ### Conclusion
49 |
50 | I want to emphasize again that the learning never really stops but also that you've come a long way. Stick around for the next few courses and you'll take a good core and build some very interesting things. In the HTML/CSS course, you'll learn to make your Rails applications look like actual websites instead of text dumps. In the Javascript course, you'll learn how to take everyday web apps and make them much faster and more dynamic in the browser.
51 |
52 | So there's a lot left to go but it gets better and better. Stick with it!
53 |
54 | ### Additional Resources
55 | This section contains helpful links to other content. It isn't required, so consider it supplemental for if you need to dive deeper into something.
56 |
57 | * [How I Learned Ruby on Rails](https://medium.com/how-i-learned-ruby-rails/e08c94e2a51e) by Richardson Dackam
58 | * A [listing of Rails tutorials](https://tutorials.railsapps.org/rails-tutorial) from Daniel Kehoe of RailsApps
59 | * A list of some slightly [longer Rails Tutorials by Jumpstart Lab](http://tutorials.jumpstartlab.com/)
60 | * The [University of Texas Rails Course Online](http://schneems.com/ut-rails) (incomplete/dated?)
61 | * [Rails Cheat Sheet](http://pragtob.github.io/rails-beginner-cheatsheet/index.html) by Tobias Pfeiffer
62 | * [Rails Best Practices list](http://rails-bestpractices.com/)
63 | * [Building a Simple CMS in Sinatra Tutorial](http://www.sitepoint.com/a-simple-content-management-system-in-sinatra/)
64 | * [Why not to hardcode your application's secret token in production](http://daniel.fone.net.nz/blog/2013/05/20/a-better-way-to-manage-the-rails-secret-token/)
65 | * [How I Test by Ryan Bates](http://railscasts.com/episodes/275-how-i-test)
66 | * [Why use the `%Q` methods?](http://stackoverflow.com/questions/10144543/what-is-the-use-case-for-rubys-q-q-quoting-methods)
67 | * [Ruby on Rails Tutor](http://rubyonrailstutor.github.io/) has free videos that highlight specific sections of Rails.
68 | * [Mackenzie Child 12 apps in 12 weeks](https://medium.com/ruby-on-rails/how-i-finally-learned-rails-95e9b832675b#.mw99m5wat)
69 |
--------------------------------------------------------------------------------
/rails_programming/apis_mailers_advanced_topics/mailers.md:
--------------------------------------------------------------------------------
1 | ### Introduction
2 |
3 | Email is under-appreciated in its complexity on many levels. I certainly didn't think about it much until I needed to actually bake it into my apps. But what goes on behind the scenes, especially after hitting "send", is pretty impressive... lots of bouncing around the architecture of the internet, bypassing firewalls, and navigating SPAM filters. Luckily all this can be handled by some specialty Heroku add-ons or other third parties.
4 |
5 | The actual production of the email is what we'll cover here... i.e. how do you make that special "thanks for signing up, userX" email. Creating and sending email is actually conceptually similar to rendering views and shipping them to the web browser, at least from your point of view as a Rails dev. In this lesson we'll cover that process and you'll get a chance to send your own emails in the projects.
6 |
7 | ### Learning Outcomes
8 | Look through these now and then use them to test yourself after doing the assignment.
9 |
10 | * What is a mailer?
11 | * How do you set up a new mailer from the command line?
12 | * How are mailers similar to controllers? To models?
13 | * How do you pass instance variables from the mailer to the mailer view?
14 | * Why do you need both a text and HTML version of your mails?
15 | * How do you send an email directly from the Rails console?
16 | * How can you take advantage of add-ons and third-party programs to send email?
17 | * What is the `letter_opener` gem good for?
18 | * Why can't you use `*_path` link helpers in mailer views?
19 | * How do you style up a pretty looking HTML email?
20 |
21 | ### Brief Overview
22 |
23 | To send email, Rails uses the `ActionMailer` gem (one of the 7 core gems that make it up). A mailer acts similar to a model and controller baked into one. You will create a new mailer just like you would with a new controller (`$ rails generate mailer SomeName`). You'll then set up methods for each email that you might want it to send (e.g. welcome email, followup email, thanks for purchasing email...). Sounds a lot like controller actions, eh?
24 |
25 | In each method, you need to identify the key fields and any variables that will be used in the text of the email (like the user's name). The actual email itself is just a view. They live in the `app/views` folder along with the rest of them, though in a sub-folder named after the mailer.
26 |
27 | One distinction between mailer views and "normal" views is that emails typically have two separate formats -- HTML and text. Most people will view the HTML version but some will have HTML disabled and so they will need the text version in order to see your email. Having the two versions also helps you bypass SPAM filters. You'll quickly find that many things you do regarding email have to do with getting past the dreaded SPAM filter.
28 |
29 | So you set up one view called, for instance, `app/views/user_mailer/welcome_email.html.erb` and another called `app/views/user_mailer/welcome_email.text.erb`. It is kind of annoying having to manage two different versions of the same email but such is the cost of accessibility and SPAM fighting. Another thing you'll notice is that you can use ERB (or HAML etc) preprocessing on your emails too. Just like any other view, it allows you to dynamically create content that includes things like your user's name or non-sensitive account information. This is all based on which variables you pass the view from your mailer, just like passing a controller action's instance variables to a normal view.
30 |
31 | Mailers are actually a two-step process -- first you build the email itself and then you call the `#deliver_now` (or `#deliver_now!`) method to actually send it. One way that mailers are different from controllers is that you call them directly instead of working through the router. When you want to call the welcome email, you will write `UserMailer.welcome_email(@user)`, which returns the email object. Deliver it by calling the `#deliver_now` method on that object, for a total chain of: `UserMailer.welcome_email(@user).deliver_now`.
32 |
33 | As long as your view files (the HTML and text) are named the same as your mailer methods, Rails will find everything okay and will send the email (with both the HTML and text parts) using whichever addon you've chosen to use for sending it. The Rails Guide reading does a good job walking through the process, so you'll get a chance to see it in action there.
34 |
35 | ### Callbacks
36 |
37 | Mailers allow you to use callbacks just like a normal controller, for instance the `after_action` callback. They will run after the email is *generated*, not after it is *sent*, so you'll be able to modify parts of the email if you need to. You get access to the instance variables of the mail instance, so you can use the `@user` variable as a part of your logic.
38 |
39 | ### Email Addons
40 |
41 | In the reading you'll see how to send mail using your Gmail account, but if you're building a real application you'll obviously want something a bit more robust. There are several leading players in the space of sending your email for you. Their whole job is to handle getting your mail delivered and opened so you can focus on building your application. They are usually available as add-ons to Heroku (like New Relic was, which we saw in the lesson on deployment), so all you need to do is properly configure them.
42 |
43 | [SendGrid](https://addons.heroku.com/sendgrid#1500000) is the add-on used with this website for delivering welcome emails and the like and it's pretty straightforward. See the [documentation here](https://devcenter.heroku.com/articles/sendgrid), which includes instructions for how you should set up your `config/environment.rb` file to get ActionMailer to interface with them. You will need to use environment variables (or the `figaro` gem) again to avoid hard coding your password and username.
44 |
45 | Pricing for this, as most things, is free up until a certain usage tier. While you're building toy apps, it will do just fine. Other options are out there like [MailGun](https://addons.heroku.com/mailgun)—check out others in the email section of the [Heroku Addon Store](https://addons.heroku.com/#email-sms).
46 |
47 | You'll get a chance to play with mailers and addons in the projects.
48 |
49 | ### Letter Opener
50 |
51 | One key thing to note is that you don't want to fire off a bunch of emails when you're testing your app in the development environment. That's not just bad practice, it can make your users pretty unhappy and get you put on SPAM lists. No bueno. But you do want to make sure the email function is working properly. Luckily, there's a simple solution which is quite useful.
52 |
53 | The [Letter Opener gem (see docs)](https://github.com/ryanb/letter_opener), put in your `development` group of the Gemfile, will take your emails and display them in the web browser for you whenever they would otherwise be sent. You just switch a config setting in your `config/environments/development.rb` file and you're good to go. Sweet.
54 |
55 | ### Email Wisdom
56 |
57 | * Email is SLOW! It can take 1-2 seconds per email to send, so don't make your main application do it when you're serving a whole bunch of them because then anyone trying to access it will be shut out.
58 | * Make sure you use full URLs in any links in your mailer (so `_url` not `_path` helper methods), since the user will be opening the email and clicking the link at an external source. In your `config/environments/production.rb` file you'll want to make sure to specify your website's host name using something like `config.action_mailer.default_url_options = { :host => 'yourapp.com' }`. If it's not set, you may get an error message about your host or your links may look funny.
59 | * The `#deliver!` method will throw an error on delivery failure whereas `#deliver` will return false (failing silently).
60 | * When styling your email HTML, you won't have access to any stylesheets so you'll need to do all the styling either inline or using `