Skip to content

Commit 5e59384

Browse files
committed
update readme and add products
1 parent 53543b1 commit 5e59384

18 files changed

Lines changed: 340 additions & 3 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@
1313
# Ignore all logfiles and tempfiles.
1414
/log/*.log
1515
/tmp
16+
17+
18+
.DS_Store

README.md

Lines changed: 186 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ If any of this is new or confusing information please review Week 2, or simply t
110110

111111
Homework: You should have at least one user and one product in your database at this point and time, if you don't please start over from the beginning. You will now run a script that will generate many fake users and many fake products, the technical term for this is "seeding" the database with fake data. We are using a gem called 'ffaker' but you don't need to know that. In the terminal (not the rails console) run this command:
112112

113-
$ ruby lib/fake_data.rb
113+
$ rake fake:data
114114

115115
If you got no output at all, check you are in the rails directory by running `ls`
116116

@@ -472,11 +472,195 @@ Here we are telling our app that the path `/products/new` should map to our prod
472472

473473
Now that we've got our Model in place, our Controller, and our Routes for this view file (M_Cr) we need to finish our view before our (MVCr) for this action is complete. Open up `app/views/products/new.html.erb` here we will add a form where a visitor to our website can add a product.
474474

475-
In the last homework we added a simple form, we will do the same thing here.
475+
In the last homework we added a simple form, we will do the same thing here, when a user submits the form the will be adding data to our database, we want the form to use a POST action. We will want to include the name of our product as well as a price. First we will start off by adding the form tags
476476

477+
<form method='post' action='/products' >
477478

479+
</form>
478480

481+
This tells the browser that when we submit this form it will submit to the `/products` path using a method of post. Remember we can re-use the /products path that we used for index, since this is a POST action and index uses a GET. (Routes = URL + HTTP Method). Next we want to add a text field:
479482

483+
<form method='post' action='/products' >
484+
<input name="product[name]" size="30" type="text">
485+
</form>
480486

487+
Here the type of the input field is "text" and it will be 30 characters wide. When we submit this form we can find the result of this field in the `product[name]`, Rails will translate this to `params[:product][:name]` for us for ease of use. Refresh the page.
488+
489+
Add a label above the text field
490+
491+
<form method='post' action='/products' >
492+
<label>Name</label><br>
493+
<input name="product[name]" size="30" type="text">
494+
</form>
495+
496+
Great now add a submit button:
497+
498+
<form method='post' action='/products' >
499+
<label>Name</label><br>
500+
<input name="product[name]" size="30" type="text">
501+
502+
<input type="submit" value="Create Product">
503+
</form>
504+
505+
Here we are adding an input element with type='submit' which indicates it will be the submit button. We can control the text in the button by change the text inside of it's value. For now it isn't important to memorize the syntax of this html just be able to read it and modify it.
506+
507+
Lets add our price and then test out the form:
508+
509+
510+
<form method='post' action='/products' >
511+
<label>Name</label><br>
512+
<input name="product[name]" size="30" type="text">
513+
514+
<br />
515+
<label>Price</label><br>
516+
<input name="product[price]" type="number">
517+
518+
<br />
519+
<input type="submit" value="Create Product">
520+
</form>
521+
522+
523+
Again we are storing the price inside of the product hash (in rails terms) so we can get to it by using `params[:product][:price]` later. Instead of a text type we are designating this as a number field.
524+
525+
526+
527+
We've got everything we need to build a product (minus a user which is out of scope for this example). Fill out the form and hit "Create Product" and...
528+
529+
530+
We get another routing error. We only implemented the view for the form, but didn't write any of the logic for persisting (storing) the data to the database. Lets do that now.
531+
532+
Open up `config/routes.rb` and add this line:
533+
534+
535+
post '/products' => 'products#create'
536+
537+
You will then need to make a new view `create.html.erb` in the folder `app/views/products/` (hopefully, you're noticing a trend), the full path will be `app/views/products/create.html.erb`. Open this file and add this to it:
538+
539+
540+
<h2>Create View</h2>
541+
542+
<%= params.inspect %>
543+
544+
545+
Refresh the page, you will be prompted if you are sure. This is a safety feature due to the browser recognizing the POST action. Most POST actions such as ordering an item off of amazon or ebay are intended to happen only once. Refreshing the request could lead to unwanted duplicate orders. For now we haven't written the logic to persist to the database, so click okay.
546+
547+
If you accidentally close this page just go back to `/products/new` and re-submit the form.
548+
549+
You should see something like this:
550+
551+
Create View
552+
553+
{"product"=>{"name"=>"bird house", "price"=>"999"}, "controller"=>"products", "action"=>"create"}
554+
555+
So here we have all the data we need to create our product. Remember in the console we can run:
556+
557+
> Product.create(:name => "bird house", :price => 999)
558+
559+
To create a product. Since params is a hash we can change our view to this
560+
561+
<h2>Create View</h2>
562+
563+
<%= params[:product].inspect %>
564+
565+
Now refresh the page, you should see something like this:
566+
567+
{"name"=>"bird house", "price"=>"999"}
568+
569+
This looks dangerously close to what we needed to pass to `Product.create` in order to actually save our info to the database. Lets try it out. In the bottom of your view add this line:
570+
571+
<%= Product.create(params[:product]).inspect %>
572+
573+
Refresh the page, now you should see something like this
574+
575+
#<Product id: 225, user_id: nil, name: "bird house", price: 999, created_at: "2012-06-23 22:15:09", updated_at: "2012-06-23 22:15:09">
576+
577+
If our product has an id, that means it saved. We now have that product in our database. To prove it you can open a `$ rails console` and run this (replacing your id):
578+
579+
> Product.where(:id => 225).first
580+
> #<Product id: 225, user_id: nil, ... >
581+
582+
Thats pretty cool, we just built a way to insert data into our database from a website, it might not be pretty but it works!!
583+
584+
Refresh the page again, and the product id should be incremented:
585+
586+
#<Product id: 226, user_id: nil, name: "bird house", price: 999, created_at: "2012-06-23 22:16:48", updated_at: "2012-06-23 22:16:48">
587+
588+
But that doesn't seem right now we have two products with duplicate names. Lets tell Rails that shouldn't happen. We'll tell rails to validate the uniqueness of the name field on products. Open your product model
589+
`app/models/product.rb` and add this code in:
590+
591+
validates :name, :uniqueness => true
592+
593+
Now refresh the page and the product id is gone! The duplicate product wasn't saved.
594+
595+
<Product id: nil, user_id: nil, name: "bird house", price: 999, created_at: nil, updated_at: nil>
596+
597+
To understand why it isn't saving lets change our view code a bit, first replace this line:
598+
599+
<%= Product.create(params[:product]).inspect %>
600+
601+
With this:
602+
603+
<% product = Product.create(params[:product]) %>
604+
<%= product.inspect %>
605+
606+
Refresh the page, and you should see the exact same thing. Now add this to the bottom of your view
607+
608+
<br />
609+
<%= product.errors.inspect %>
610+
611+
You should see an output that looks similar to this:
612+
613+
#<ActiveModel::Errors:0x007fdc62bb1b38 @base=#<Product id: nil, user_id: nil, name: "bird house",
614+
price: 999, created_at: nil, updated_at: nil>,
615+
@messages={:name=>["has already been taken"]}>
616+
617+
618+
Since we told rails that all valid products have a unique name, and rails will only save valid products, this error prevented the product from getting saved. The message tells us why `:name=>["has already been taken"]`, the name has already been taken.
619+
620+
Since this is a common occurrence we will need to check for this type of a thing in our code.
621+
622+
Rather than use the `create` command in our code we can make a new object and then run save on it (create does both at the same time). Lets remove this line:
623+
624+
<% product = Product.create(params[:product]) %>
625+
626+
and replace it with this
627+
628+
<% product = Product.new(params[:product]) %>
629+
<%= product.save %>
630+
631+
Which essentially does the same thing, but the out put of `product.save` is a boolean (true or false) we can use this to show different message to our user if it saves or not.
632+
633+
Add this to your view:
634+
635+
<% if product.save %>
636+
<h2> Congrats You Created a New Product</h2>
637+
Your product looks like <%= product.inspect %>
638+
<% else %>
639+
<h2>Your product was not saved!! </h2>
640+
<%= product.errors.full_messages %>
641+
Please go back in your browser and fix the problem
642+
<% end %>
643+
644+
Refresh the page again you should see
645+
646+
Your product was not saved!!
647+
["Name has already been taken"]
648+
Please go back in your browser and fix the problem
649+
650+
Thats much more useful to our user.
651+
652+
653+
Homework:
654+
655+
Commit results to git & smile
656+
657+
658+
## Done
659+
660+
Congrats, you're done, you've come pretty far since last week. Last week you were barely scratching the surface of generating html with ruby, and now you've completely written half of a CRUD server (for products). Think about that, you took data from a database and brought it to life in the browser. You then turned around and figured out a way to send data from a web form back to your database. That's pretty amazing. We also didn't use much Rails magic, for our models we used vanilla ActiveRecord Ruby objects, for our views we used (mostly) vanilla Ruby ERB. We did use this special Routes thing, and this special controller thing, but not very much.
661+
662+
Now that you understand the basics of sending and retrieving data from a database, next week we can start to use some more rails practices to clean up your code and make life a little easier for yourself. If you were curious and decided to poke around in the views and controller for User, you might be a little surprised by how different it is, don't worry most of that is organization and is quite a bit harder to understand without the fundamentals we've just experienced.
663+
664+
The most important thing to take away from this MVCr exercise is that you can (and should) build everything incrementally. It's okay to not understand the bigger picture until after you're done. Taking many small steps and checking yourself after each is the best way to stay on course, no matter what the activity is.
481665

482666

31.3 KB
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Place all the behaviors and hooks related to the matching controller here.
2+
# All this logic will automatically be available in application.js.
3+
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Place all the styles related to the products controller here.
2+
// They will automatically be included in application.css.
3+
// You can use Sass (SCSS) here: http://sass-lang.com/
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class ProductsController < ApplicationController
2+
end

app/helpers/products_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module ProductsHelper
2+
end

app/models/product.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class Product < ActiveRecord::Base
2+
belongs_to :user
3+
attr_accessible :name, :price
4+
end

app/models/user.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
class User < ActiveRecord::Base
2+
has_many :products
23
attr_accessible :job_title, :name
34
end

app/views/products/index.html.erb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<h2> I AMA View </h2>
2+
<p>
3+
Find me in <%= Rails.root.join("app", "views", "products", __FILE__ ) %>
4+
</p>

0 commit comments

Comments
 (0)