tft_rails 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/lib/generators/chapter08_09/begin/instructions.md +6 -2
  2. data/lib/generators/chapter10/begin/instructions.md +11 -1
  3. data/lib/generators/chapter10/begin/templates/spec/controllers/users_controller_10_spec.rb +10 -1
  4. data/lib/generators/chapter11_1/begin/USAGE +24 -0
  5. data/lib/generators/{chapter11 → chapter11_1}/begin/begin_generator.rb +6 -2
  6. data/lib/generators/chapter11_1/begin/instructions.md +39 -0
  7. data/lib/generators/chapter11_1/begin/templates/lib/tasks/same_data.rake +29 -0
  8. data/lib/generators/chapter11_1/begin/templates/spec/models/microposts_11_1_spec.rb +45 -0
  9. data/lib/generators/chapter11_1/begin/templates/spec/models/user_11_1_spec.rb +30 -0
  10. data/lib/generators/chapter11_1/solutions/USAGE +18 -0
  11. data/lib/generators/chapter11_1/solutions/snippets/migration_create_microposts.rb +17 -0
  12. data/lib/generators/chapter11_1/solutions/solutions_generator.rb +25 -0
  13. data/lib/generators/chapter11_1/solutions/templates/app/model/micropost.rb +10 -0
  14. data/lib/generators/chapter11_1/solutions/templates/app/model/user.rb +14 -0
  15. data/lib/generators/chapter11_1/solutions/templates/spec/factories.rb +16 -0
  16. data/lib/generators/chapter11_2/begin/USAGE +24 -0
  17. data/lib/generators/chapter11_2/begin/begin_generator.rb +30 -0
  18. data/lib/generators/chapter11_2/begin/instructions.md +35 -0
  19. data/lib/generators/chapter11_2/begin/snippets/custom.css +59 -0
  20. data/lib/generators/chapter11_2/begin/templates/app/views/users/show.html.erb +23 -0
  21. data/lib/generators/chapter11_2/begin/templates/spec/controllers/users_controller_11_2_spec.rb +31 -0
  22. data/lib/generators/chapter11_2/solutions/USAGE +18 -0
  23. data/lib/generators/{chapter11 → chapter11_2}/solutions/solutions_generator.rb +1 -1
  24. data/lib/generators/chapter11_2/solutions/templates/app/controllers/users_controller.rb +29 -0
  25. data/lib/generators/chapter11_2/solutions/templates/app/views/microposts/_micropost.html.erb +8 -0
  26. data/lib/generators/chapter11_2/solutions/templates/app/views/users/show.html.erb +23 -0
  27. data/lib/generators/chapter11_3/begin/begin_generator.rb +24 -0
  28. data/lib/generators/chapter11_3/begin/instructions.md +80 -0
  29. data/lib/generators/chapter11_3/begin/templates/spec/controllers/microposts_controllers_11_3_spec.rb +102 -0
  30. data/lib/generators/chapter11_3/begin/templates/spec/controllers/pages_controller_11_3_spec.rb +14 -0
  31. data/lib/generators/chapter11_3/solutions/solutions_generator.rb +8 -0
  32. metadata +32 -7
  33. data/lib/generators/chapter11/begin/instructions.md +0 -2
  34. /data/lib/generators/{chapter11 → chapter11_3}/begin/USAGE +0 -0
  35. /data/lib/generators/{chapter11 → chapter11_3}/solutions/USAGE +0 -0
@@ -7,7 +7,7 @@ Sign up
7
7
  In the adapted version of RailsTutorial that we're using
8
8
  here, the user registration and user authentication tasks
9
9
  are not implemented in the application itself, but
10
- instead handled by a gem called [Devise](devise) that is an
10
+ instead handled by a gem called [Devise][devise] that is an
11
11
  industry-standard Rails authentication solution.
12
12
 
13
13
  Devise provides its own controllers, models and views that
@@ -35,7 +35,7 @@ Devise in the gem.
35
35
  Even though the controller sits in Devise, we can still run
36
36
  our own test against it. This was done here.
37
37
 
38
- Take a look at the [Devise instructions on github](devise)
38
+ Take a look at the [Devise instructions on github][devise]
39
39
  to learn about how to customize views. That what we'll
40
40
  need to do. Hint: You'll need to run a Devise generator.
41
41
 
@@ -50,5 +50,9 @@ The Devise sign-in page meets our needs and doesn't need customizing.
50
50
  However, we need to work on the layout to make sure that it shows a "Sign out" link
51
51
  if a user is logged in, and a "Sign in" link only if a user is not logged in.
52
52
 
53
+ [Devise][devise] offers the controller methods `user_signed_in?` and `current_user`
54
+ to report on the current login status. The first method returns `true` or `false`
55
+ depending on whether a user logged in, the second returns the actual user object,
56
+ or `nil` if no user is logged in.
53
57
 
54
58
  [devise]: https://github.com/plataformatec/devise "Devise on github"
@@ -20,7 +20,7 @@ Devise offers a facility similar to what RailsTutorials implements.
20
20
 
21
21
  RailsTutorial implements an `authenticate` method, while Devise's
22
22
  version is `authenticate_user!`. Either method redirects to the sign-in
23
- page if a given user isn't logged in.
23
+ page if the user isn't logged in.
24
24
 
25
25
  Destroying users with admin privilege
26
26
  -------------------------------------
@@ -33,5 +33,15 @@ for users which, however, is only available to admin users.
33
33
 
34
34
  What are the two things we have to protect?
35
35
 
36
+ Rails Concepts Covered
37
+ ======================
38
+
39
+ * Migrations
40
+ * Factories
41
+ * Rake Tasks
42
+ * `attr_accessible`
43
+ * before_filter
44
+ * rendering partials by collection object
45
+
36
46
 
37
47
  [devise]: https://github.com/plataformatec/devise "Devise on github"
@@ -72,11 +72,20 @@ describe UsersController do
72
72
  end
73
73
 
74
74
  describe "as a non-admin user" do
75
- it "should protect the page" do
75
+ before do
76
76
  sign_in(@user)
77
+ end
78
+
79
+ it "should protect the page" do
77
80
  delete :destroy, :id => @user
78
81
  response.should redirect_to(root_path)
79
82
  end
83
+
84
+ it 'should not destroy the user' do
85
+ expect {
86
+ delete :destroy, :id => @user
87
+ }.to_not change(User,:count)
88
+ end
80
89
  end
81
90
 
82
91
  describe "as an admin user" do
@@ -0,0 +1,24 @@
1
+ Description:
2
+ Begins Test-First Teaching exercises adapted from Chapter 11.1 of the RailsTutorial by Michael Hartl.
3
+
4
+ It is assumed that this generator is run after the exercises for all prior chapters have been completed, and the solutions are implemented.
5
+
6
+ Successive chapters are expected to be run sequentially. Each chapter's generators comes with a delta
7
+ to the next chapter, in two phase, the "begin" one will create tests, the "finish" one includes the solution files.
8
+ If all tests pass, the "finish" phase is optional.
9
+
10
+ Example:
11
+ rails generate chapter11_1:begin
12
+
13
+ This copies new tests into the project, which are failing.
14
+ The student's task is to write code to make the test pass. The material covered by the tests is consistent
15
+ with Chapter 11.1 of the RailsTutorial, however adapted for Devise as authentication solution.
16
+
17
+ When you're done, and all tests pass, or you just want to skip ahead, run:
18
+
19
+ rails generate chapter11_1:solutions
20
+
21
+ This will copy solutions files into the application tree. If you already have a solution file,
22
+ you'll be prompted whether you want to overwrite your file, see the difference, or keep your file.
23
+
24
+
@@ -1,14 +1,18 @@
1
- module Chapter11
1
+ module Chapter11_1
2
2
  module Generators
3
3
  class BeginGenerator < Rails::Generators::Base
4
4
  source_root File.expand_path("../templates", __FILE__)
5
5
 
6
+ def copy_app_tree
7
+ directory(self.class.source_root, Rails.root)
8
+ end
9
+
6
10
  def generate_instructions
7
11
  require 'rdiscount'
8
12
 
9
13
  instr_md = File.expand_path('../instructions.md',self.class.source_root)
10
14
  return unless File.exists?(instr_md)
11
- dest = File.join(Rails.root,'doc','chapter10.html')
15
+ dest = File.join(Rails.root,'doc','chapter11_1.html')
12
16
  copy_file(instr_md, dest, :force => true) do |content|
13
17
  RDiscount.new(content).to_html
14
18
  end
@@ -0,0 +1,39 @@
1
+ RailsTutorial Chapter 11.1 Test-First Teaching Instructions
2
+ =========================================================
3
+
4
+ Chapter 11.1 covers adding a Micropost model and setting up an association between
5
+ User and Micropost.
6
+
7
+ When you first run `rake spec`, you'll get an error like below and no specs will execute.
8
+
9
+ uninitialized constant Micropost (NameError)
10
+
11
+ This is because we don't have this class defined yet. So, go ahead and create it using the rails generators:
12
+
13
+ rails generate model Micropost content:string user_id:integer
14
+
15
+ This will generate a migration file, a model file and a spec file for Micropost. Note that for the TFT
16
+ exercises, a spec file is already provided, and we don't need the generated spec file. So, go ahead
17
+ and _remove:_ `spec/models/micropost_spec.rb`
18
+
19
+ Add indexes to the migration file, see [Chapter 11.1.1][chapter_11_1_1]. Indices should be added for each
20
+ column that will be used for record lookup or ordering. For instance, if we want to sort Microposts by
21
+ creation date, then there should be an index on the `created_at` column. (Note that the `timestamps` migration
22
+ methods created two columns, `created_at` and `updated_at`.)
23
+
24
+ Then migrate the database:
25
+
26
+ rake db:migrate
27
+
28
+
29
+ Rails Concepts Covered
30
+ ======================
31
+
32
+ * Using the model generator (for Microposts)
33
+ * Adding indexes to the database
34
+ * Associations, has_many (dep't destroy) and belongs_to
35
+ * Validations for format
36
+ * Factories with associations
37
+ * Scopes (simple, default scope), order
38
+
39
+ [chapter_11_1_1]: http://ruby.railstutorial.org/chapters/user-microposts#sec:the_basic_model "RailsTutorial Chapter 11.1.1"
@@ -0,0 +1,29 @@
1
+ namespace :db do
2
+ desc "Fill database with sample data"
3
+ task :populate => :environment do
4
+ Rake::Task['db:reset'].invoke
5
+ make_users
6
+ end
7
+ end
8
+
9
+ def make_users
10
+ admin = User.create!(:name => "Example User",
11
+ :email => "example@railstutorial.org",
12
+ :password => "password",
13
+ :password_confirmation => "password")
14
+ admin.toggle!(:admin)
15
+ 99.times do |n|
16
+ name = Faker::Name.name
17
+ email = "example-#{n+1}@railstutorial.org"
18
+ password = "password"
19
+ User.create!(:name => name,
20
+ :email => email,
21
+ :password => password,
22
+ :password_confirmation => password)
23
+ end
24
+ User.all(:limit => 6).each do |user|
25
+ 50.times do
26
+ user.microposts.create!(:content => Faker::Lorem.sentence(5))
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Micropost do
4
+
5
+ before(:each) do
6
+ @user = Factory(:user)
7
+ @attr = { :content => "value for content" }
8
+ end
9
+
10
+ it "should create a new instance given valid attributes" do
11
+ @user.microposts.create!(@attr)
12
+ end
13
+
14
+ describe "user associations" do
15
+
16
+ before(:each) do
17
+ @micropost = @user.microposts.create(@attr)
18
+ end
19
+
20
+ it "should have a user attribute" do
21
+ @micropost.should respond_to(:user)
22
+ end
23
+
24
+ it "should have the right associated user" do
25
+ @micropost.user_id.should == @user.id
26
+ @micropost.user.should == @user
27
+ end
28
+ end
29
+
30
+ describe "validations" do
31
+
32
+ it "should require a user id" do
33
+ Micropost.new(@attr).should_not be_valid
34
+ end
35
+
36
+ it "should require nonblank content" do
37
+ @user.microposts.build(:content => " ").should_not be_valid
38
+ end
39
+
40
+ it "should reject long content" do
41
+ @user.microposts.build(:content => "a" * 141).should_not be_valid
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe User do
4
+
5
+ describe "micropost associations" do
6
+
7
+ before(:each) do
8
+ @user = Factory(:user)
9
+ @mp1 = Factory(:micropost, :user => @user, :created_at => 1.day.ago)
10
+ @mp2 = Factory(:micropost, :user => @user, :created_at => 1.hour.ago)
11
+ end
12
+
13
+ it "should have a microposts attribute" do
14
+ @user.should respond_to(:microposts)
15
+ end
16
+
17
+ it "should have the right microposts in the right order" do
18
+ @user.microposts.should == [@mp2, @mp1]
19
+ end
20
+
21
+ it "should destroy associated microposts" do
22
+ @user.destroy
23
+ [@mp1, @mp2].each do |micropost|
24
+ Micropost.find_by_id(micropost.id).should be_nil
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,18 @@
1
+ Description:
2
+ Wraps up Test-First Teaching exercises adapted from Chapter 11.1 of the RailsTutorial by Michael Hartl.
3
+
4
+ Example:
5
+ rails generate chapter11:solutions
6
+
7
+ This copies the solutions and other necessary files (e.g. images, assets, helpers, etc.) into the project
8
+ to conclude Chapter 11.1. This should make all tests pass and bring you in sync to the application at
9
+ the end of Chapter 11.1.
10
+
11
+ If you already have a solution file, you'll be prompted whether you want to overwrite your file,
12
+ see the difference, or keep your file.
13
+
14
+ It's a good idea to commit your changes to git, prior to running finish, in case you accidentally choose to
15
+ overwrite files you wanted to keep. Assuming your project is already git-controlled, you can commit changes with:
16
+
17
+ git add .
18
+ git commit -m "comment here explaining WHY you made the changes"
@@ -0,0 +1,17 @@
1
+ class CreateMicroposts < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :microposts do |t|
4
+ t.string :content
5
+ t.integer :user_id
6
+
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :microposts, :user_id
11
+ add_index :microposts, :created_at
12
+ end
13
+
14
+ def self.down
15
+ drop_table :microposts
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ module Chapter11_1
2
+ module Generators
3
+ class SolutionsGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../templates", __FILE__)
5
+
6
+ def copy_app_tree
7
+ directory(self.class.source_root, Rails.root)
8
+ end
9
+
10
+ def add_migration
11
+ found_candidate = Dir.glob(File.join(Rails.root,'db','migrate','*create_micropost*')).present?
12
+
13
+ if (found_candidate &&
14
+ yes?("We found a migration file containing the word '_admin_'. We think you have the correct migration. Do you still way to copy the solution anyway? (yes/no)", :yellow)) \
15
+ or !found_candidate
16
+
17
+ src = File.expand_path("../snippets/migration_create_microposts.rb", __FILE__)
18
+ dest = File.join(Rails.root,'db','migrate',Time.now.strftime("%Y%m%d%H%M%S")+'_create_microposts.rb')
19
+ copy_file(src,dest)
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ class Micropost < ActiveRecord::Base
2
+
3
+ belongs_to :user
4
+
5
+ default_scope order('created_at DESC')
6
+
7
+ validates :user_id, :presence => true
8
+ validates :content, :presence => true, :length => {:maximum => 140}
9
+
10
+ end
@@ -0,0 +1,14 @@
1
+ class User < ActiveRecord::Base
2
+ # Include default devise modules. Others available are:
3
+ # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
4
+ devise :database_authenticatable, :registerable,
5
+ :recoverable, :rememberable, :trackable, :validatable
6
+
7
+ validates :name, :presence => true, :length => { :maximum => 50 }
8
+
9
+ # Setup accessible (or protected) attributes for your model
10
+ attr_accessible :name, :email, :password, :password_confirmation, :remember_me
11
+
12
+ has_many :microposts, :dependent => :destroy
13
+
14
+ end
@@ -0,0 +1,16 @@
1
+ # By using the symbol ':user', we get Factory Girl to simulate the User model.
2
+ Factory.define :user do |user|
3
+ user.name "Michael Hartl"
4
+ user.email "mhartl@example.com"
5
+ user.password "foobar"
6
+ user.password_confirmation "foobar"
7
+ end
8
+
9
+ Factory.sequence :email do |n|
10
+ "person-#{n}@example.com"
11
+ end
12
+
13
+ Factory.define :micropost do |mp|
14
+ mp.content "This is a new post"
15
+ mp.association :user
16
+ end
@@ -0,0 +1,24 @@
1
+ Description:
2
+ Begins Test-First Teaching exercises adapted from Chapter 11.2 of the RailsTutorial by Michael Hartl.
3
+
4
+ It is assumed that this generator is run after the exercises for all prior chapters have been completed, and the solutions are implemented.
5
+
6
+ Successive chapters are expected to be run sequentially. Each chapter's generators comes with a delta
7
+ to the next chapter, in two phase, the "begin" one will create tests, the "finish" one includes the solution files.
8
+ If all tests pass, the "finish" phase is optional.
9
+
10
+ Example:
11
+ rails generate chapter11_2:begin
12
+
13
+ This copies new tests into the project, which are failing.
14
+ The student's task is to write code to make the test pass. The material covered by the tests is consistent
15
+ with Chapter 11.2 of the RailsTutorial, however adapted for Devise as authentication solution.
16
+
17
+ When you're done, and all tests pass, or you just want to skip ahead, run:
18
+
19
+ rails generate chapter11_2:solutions
20
+
21
+ This will copy solutions files into the application tree. If you already have a solution file,
22
+ you'll be prompted whether you want to overwrite your file, see the difference, or keep your file.
23
+
24
+
@@ -0,0 +1,30 @@
1
+ module Chapter11_2
2
+ module Generators
3
+ class BeginGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../templates", __FILE__)
5
+
6
+ def copy_app_tree
7
+ directory(self.class.source_root, Rails.root)
8
+ end
9
+
10
+ def insert_css
11
+ src = File.expand_path("../snippets/custom.css", __FILE__)
12
+ dest = File.join(Rails.root,'public','stylesheets','custom.css')
13
+ insert_into_file(dest, File.binread(src), :before => /\Z/) # insert before end
14
+ end
15
+
16
+ def generate_instructions
17
+ require 'rdiscount'
18
+
19
+ instr_md = File.expand_path('../instructions.md',self.class.source_root)
20
+ return unless File.exists?(instr_md)
21
+ dest = File.join(Rails.root,'doc','chapter11_2.html')
22
+ copy_file(instr_md, dest, :force => true) do |content|
23
+ RDiscount.new(content).to_html
24
+ end
25
+ say_status('Note',"Now open file://#{dest} in your web browser for instructions", :cyan)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ RailsTutorial Chapter 11.2 Test-First Teaching Instructions
2
+ ===========================================================
3
+
4
+ When you first run `rake spec`, you'll get an error like below and no specs will execute.
5
+
6
+ uninitialized constant MicropostsController (NameError)
7
+
8
+ This is because we don't have this class defined yet. So, go ahead and create it using the rails generators:
9
+
10
+ _Note_: **Microposts is in plural here** (Rails convention for controllers).
11
+
12
+ rails g controller Microposts --controller-specs=false --view-specs=false --helper-specs=false
13
+
14
+ Note: The controller generatore (unlike the model generator), we can instruct to suppress
15
+ the spec files.
16
+
17
+ For the particular markup to make the micropost display look pretty,
18
+ refer to Rails Tutorial [Chapter 11.2.1][chapter_11_2_1]
19
+
20
+ Extra Credit
21
+ ------------
22
+
23
+ Review how pagination for the users index page was done in Chapter 10.
24
+ Apply the same technique to the microposts displayed on the user show page.
25
+
26
+ Rails Concepts Covered
27
+ ======================
28
+
29
+ * Using controller generator
30
+ * Testing for instance variables in the controller
31
+ * More association methods, :empty?, :count
32
+ * Rendering partials by object collection
33
+ * Time helper
34
+
35
+ [chapter_11_2_1]: http://ruby.railstutorial.org/chapters/user-microposts#sec:augmenting_the_user_show_page "Chapter 11.2.1"
@@ -0,0 +1,59 @@
1
+
2
+ /************************/
3
+ /* Through Chapter 11.2 */
4
+ /************************/
5
+
6
+ h1.micropost {
7
+ margin-bottom: 0.3em;
8
+ }
9
+
10
+ table.microposts {
11
+ margin-top: 1em;
12
+ }
13
+
14
+ table.microposts tr {
15
+ height: 70px;
16
+ }
17
+
18
+ table.microposts tr td.gravatar {
19
+ border-top: 1px solid #ccc;
20
+ vertical-align: top;
21
+ width: 50px;
22
+ }
23
+
24
+ table.microposts tr td.micropost {
25
+ border-top: 1px solid #ccc;
26
+ vertical-align: top;
27
+ padding-top: 10px;
28
+ }
29
+
30
+ table.microposts tr td.micropost span.timestamp {
31
+ display: block;
32
+ font-size: 85%;
33
+ color: #666;
34
+ }
35
+
36
+ div.user_info img {
37
+ padding-right: 0.1em;
38
+ }
39
+
40
+ div.user_info a {
41
+ text-decoration: none;
42
+ }
43
+
44
+ div.user_info span.user_name {
45
+ position: absolute;
46
+ }
47
+
48
+ div.user_info span.microposts {
49
+ font-size: 80%;
50
+ }
51
+
52
+ form.new_micropost {
53
+ margin-bottom: 2em;
54
+ }
55
+
56
+ form.new_micropost textarea {
57
+ height: 4em;
58
+ margin-bottom: 0;
59
+ }
@@ -0,0 +1,23 @@
1
+ <table class="profile" summary="Profile information">
2
+ <tr>
3
+ <td class="main">
4
+ <h1>
5
+ <%= gravatar_for @user %>
6
+ <%= @user.name %>
7
+ </h1>
8
+ <%# only if microposts exist, should the content below be rendered %>
9
+ <table class="microposts" summary="User microposts">
10
+ (The list of microposts should show up here, if there are any.)
11
+ </table>
12
+ <!--<%#= (pagination here, optional) %>-->
13
+ <%# end %>
14
+ </td>
15
+ <td class="sidebar round">
16
+ <strong>Name</strong> <%= @user.name %>
17
+ <br/>
18
+ <strong>URL</strong> <%= link_to user_path(@user.id), @user %>
19
+ <br/>
20
+ <strong>Microposts</strong> (The total count of Microposts should be here.)
21
+ </td>
22
+ </tr>
23
+ </table>
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe UsersController do
4
+
5
+ describe "GET 'show'" do
6
+
7
+ before(:each) do
8
+ @user = Factory(:user)
9
+ @mp1 = Factory(:micropost, :user => @user, :content => "Foo bar", :created_at => 1.day.ago)
10
+ @mp2 = Factory(:micropost, :user => @user, :content => "Baz quux",:created_at => 2.days.ago)
11
+ end
12
+
13
+ it 'should populate the @microposts instance variable' do
14
+ get :show, :id => @user.id
15
+ # Note: in a controller test, we can access instance variables
16
+ # of the controller by using the assigns[] function
17
+ assigns[:microposts].should == [@mp1,@mp2]
18
+ end
19
+
20
+ describe "HTML output" do
21
+ render_views
22
+
23
+ it "should show the user's microposts" do
24
+ get :show, :id => @user.id
25
+ response.should have_selector("span.content", :content => @mp1.content)
26
+ response.should have_selector("span.content", :content => @mp2.content)
27
+ end
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,18 @@
1
+ Description:
2
+ Wraps up Test-First Teaching exercises adapted from Chapter 11.2 of the RailsTutorial by Michael Hartl.
3
+
4
+ Example:
5
+ rails generate chapter11:solutions
6
+
7
+ This copies the solutions and other necessary files (e.g. images, assets, helpers, etc.) into the project
8
+ to conclude Chapter 11.2. This should make all tests pass and bring you in sync to the application at
9
+ the end of Chapter 11.2.
10
+
11
+ If you already have a solution file, you'll be prompted whether you want to overwrite your file,
12
+ see the difference, or keep your file.
13
+
14
+ It's a good idea to commit your changes to git, prior to running finish, in case you accidentally choose to
15
+ overwrite files you wanted to keep. Assuming your project is already git-controlled, you can commit changes with:
16
+
17
+ git add .
18
+ git commit -m "comment here explaining WHY you made the changes"
@@ -1,4 +1,4 @@
1
- module Chapter11
1
+ module Chapter11_2
2
2
  module Generators
3
3
  class SolutionsGenerator < Rails::Generators::Base
4
4
  end
@@ -0,0 +1,29 @@
1
+ class UsersController < ApplicationController
2
+
3
+ before_filter :authenticate_user!, :only => [:index, :destroy]
4
+ before_filter :admin_user, :only => :destroy
5
+
6
+ def show
7
+ @user = User.find(params[:id])
8
+ @microposts = @user.microposts.paginate(:page => params[:page])
9
+ @title = @user.name
10
+ end
11
+
12
+ def index
13
+ @title = "All users"
14
+ @users = User.paginate(:page => params[:page])
15
+ end
16
+
17
+ def destroy
18
+ User.find(params[:id]).destroy
19
+ flash[:notice] = "User destroyed."
20
+ redirect_to users_path
21
+ end
22
+
23
+ private
24
+
25
+ def admin_user
26
+ redirect_to(root_path) unless current_user.admin?
27
+ end
28
+
29
+ end
@@ -0,0 +1,8 @@
1
+ <tr>
2
+ <td class="micropost">
3
+ <span class="content"><%= micropost.content %></span>
4
+ <span class="timestamp">
5
+ Posted <%= time_ago_in_words(micropost.created_at) %> ago.
6
+ </span>
7
+ </td>
8
+ </tr>
@@ -0,0 +1,23 @@
1
+ <table class="profile" summary="Profile information">
2
+ <tr>
3
+ <td class="main">
4
+ <h1>
5
+ <%= gravatar_for @user %>
6
+ <%= @user.name %>
7
+ </h1>
8
+ <% unless @user.microposts.empty? %>
9
+ <table class="microposts" summary="User microposts">
10
+ <%= render @microposts %>
11
+ </table>
12
+ <%= will_paginate @microposts %>
13
+ <% end %>
14
+ </td>
15
+ <td class="sidebar round">
16
+ <strong>Name</strong> <%= @user.name %>
17
+ <br/>
18
+ <strong>URL</strong> <%= link_to user_path(@user.id), @user %>
19
+ <br/>
20
+ <strong>Microposts</strong> <%= @user.microposts.count %>
21
+ </td>
22
+ </tr>
23
+ </table>
@@ -0,0 +1,24 @@
1
+ module Chapter11_3
2
+ module Generators
3
+ class BeginGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../templates", __FILE__)
5
+
6
+ def copy_app_tree
7
+ directory(self.class.source_root, Rails.root)
8
+ end
9
+
10
+ def generate_instructions
11
+ require 'rdiscount'
12
+
13
+ instr_md = File.expand_path('../instructions.md',self.class.source_root)
14
+ return unless File.exists?(instr_md)
15
+ dest = File.join(Rails.root,'doc','chapter11_3.html')
16
+ copy_file(instr_md, dest, :force => true) do |content|
17
+ RDiscount.new(content).to_html
18
+ end
19
+ say_status('Note',"Now open file://#{dest} in your web browser for instructions", :cyan)
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,80 @@
1
+ RailsTutorial Chapter 11.3 Test-First Teaching Instructions
2
+ ===========================================================
3
+
4
+ Controller
5
+ ----------
6
+
7
+ In this sub-chapter we're adding controllers and views to create and delete
8
+ microposts.
9
+
10
+ When you first run `rake spec`, you'll get an error like below and no specs will execute.
11
+
12
+ uninitialized constant MicropostsController (NameError)
13
+
14
+ This is because we don't have this class defined yet. So, go ahead and create it using the rails generators:
15
+
16
+ _Note_: **Microposts is in plural here** (Rails convention for controllers).
17
+
18
+ rails g controller Microposts --controller-specs=false --view-specs=false --helper-specs=false
19
+
20
+ Note: The controller generatore (unlike the model generator), we can instruct to suppress
21
+ the spec files.
22
+
23
+ Routing
24
+ -------
25
+
26
+ The next error will have to do with routing. Add a resource route for microposts.
27
+ Consider which actions are needed, by checking what tests case exist and
28
+ by carefully reading the test failure messages.
29
+ Then use the `:only => [...]` option to restrict the routes generated.
30
+
31
+ Views
32
+ -----
33
+
34
+ The tests will fail then complaining of missing view templates. Before you
35
+ go on to create micropost view templates, view the design mock-up
36
+ in Rails Tutorial [Chapter 11.3.2][chapter_11_3_2]. What view template
37
+ should hold the form fields to entering a micropost? If not a micropost
38
+ view, how can we tell the controller to render another view?
39
+
40
+ For a reference on the view templates, check [Listing 11.27][listing_11_27],
41
+ [Listing 11.28][listing_11_28] and [Listing 11.29][listing_11_29]
42
+
43
+ Controller Actions
44
+ ------------------
45
+
46
+ Next you'll need to implement the controller actions. For `create` there
47
+ are two branches, one for when saving the new record succeeds, i.e.
48
+ when validations pass, and one for when it fails.
49
+
50
+ Also, review the test cases carefully and consider if any of the actions
51
+ are supposed to be access controlled. Some test failures may look a bit
52
+ obscure when access control is not implemented.
53
+
54
+ The `destroy` action needs to make sure that only the use who created
55
+ the post can also delete it. Consider using the association methods
56
+ to constrain the seach for microposts to the currently signed-in user.
57
+
58
+ Display
59
+ -------
60
+
61
+ Take a moment to think about how you could show the user's posts
62
+ on the home page. There is no test for this, take a stab at it with
63
+ the tools and knowledge you now have.
64
+
65
+ Rails Concepts Covered
66
+ ======================
67
+
68
+ * Using controller generator
69
+ * Resource routes
70
+ * Controller render method
71
+ * Difference render & redirect, also redirect_to :back
72
+ * Access control
73
+ * Accessing records through an association (Association Proxy Methods)
74
+ * Controller create/update action pattern: success/failure branches
75
+ * find_by_id vs. find
76
+
77
+ [chapter_11_3_2]: http://ruby.railstutorial.org/chapters/user-microposts#sec:creating_microposts "Chapter 11.3.2"
78
+ [listing_11_27]: http://ruby.railstutorial.org/chapters/user-microposts#code:microposts_home_page "Listing 11.27"
79
+ [listing_11_28]: http://ruby.railstutorial.org/chapters/user-microposts#code:micropost_form "Listing 11.28"
80
+ [listing_11_29]: http://ruby.railstutorial.org/chapters/user-microposts#code:user_info "Listing 11.29"
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe MicropostsController do
4
+ render_views
5
+
6
+ describe "access control" do
7
+
8
+ it "should deny access to 'create'" do
9
+ post :create
10
+ response.should redirect_to(new_user_session_path)
11
+ end
12
+
13
+ it "should deny access to 'destroy'" do
14
+ delete :destroy, :id => 1
15
+ response.should redirect_to(new_user_session_path)
16
+ end
17
+ end
18
+
19
+ describe "POST 'create'" do
20
+
21
+ before(:each) do
22
+ @user = Factory(:user)
23
+ sign_in(@user)
24
+ end
25
+
26
+ describe "failure" do
27
+
28
+ before(:each) do
29
+ @attr = { :content => "" }
30
+ end
31
+
32
+ it "should not create a micropost" do
33
+ lambda do
34
+ post :create, :micropost => @attr
35
+ end.should_not change(Micropost, :count)
36
+ end
37
+
38
+ it "should render the home page" do
39
+ post :create, :micropost => @attr
40
+ response.should render_template('pages/home')
41
+ end
42
+ end
43
+
44
+ describe "success" do
45
+
46
+ before(:each) do
47
+ @attr = { :content => "Lorem ipsum" }
48
+ end
49
+
50
+ it "should create a micropost" do
51
+ lambda do
52
+ post :create, :micropost => @attr
53
+ end.should change(Micropost, :count).by(1)
54
+ end
55
+
56
+ it "should redirect to the home page" do
57
+ post :create, :micropost => @attr
58
+ response.should redirect_to(root_path)
59
+ end
60
+
61
+ it "should have a flash message" do
62
+ post :create, :micropost => @attr
63
+ flash[:success].should =~ /micropost created/i
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "DELETE 'destroy'" do
69
+
70
+ describe "for an unauthorized user" do
71
+
72
+ before(:each) do
73
+ @user = Factory(:user)
74
+ wrong_user = Factory(:user, :email => Factory.next(:email))
75
+ sign_in(wrong_user)
76
+ @micropost = Factory(:micropost, :user => @user)
77
+ end
78
+
79
+ it "should deny access" do
80
+ delete :destroy, :id => @micropost
81
+ response.should redirect_to(root_path)
82
+ end
83
+ end
84
+
85
+ describe "for an authorized user" do
86
+
87
+ before(:each) do
88
+ @user = Factory(:user)
89
+ sign_in(@user)
90
+ @micropost = Factory(:micropost, :user => @user)
91
+ request.env["HTTP_REFERER"] = user_path(@user) # for redirect_to :back
92
+ end
93
+
94
+ it "should destroy the micropost" do
95
+ lambda do
96
+ delete :destroy, :id => @micropost
97
+ end.should change(Micropost, :count).by(-1)
98
+ end
99
+ end
100
+ end
101
+
102
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe PagesController do
4
+
5
+ before do
6
+ @user = Factory(:user)
7
+ sign_in @user
8
+ end
9
+
10
+ it 'home page should instantiate a new micropost instance' do
11
+ get :home
12
+ assigns[:micropost].should_not be_nil
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ module Chapter11_3
2
+ module Generators
3
+ class SolutionsGenerator < Rails::Generators::Base
4
+
5
+
6
+ end
7
+ end
8
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: tft_rails
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.6.0
5
+ version: 0.6.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Wolfram Arnold
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-07-21 00:00:00 Z
13
+ date: 2011-07-22 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rdiscount
@@ -33,6 +33,13 @@ extra_rdoc_files: []
33
33
 
34
34
  files:
35
35
  - lib/tft_rails.rb
36
+ - lib/generators/chapter11_3/begin/USAGE
37
+ - lib/generators/chapter11_3/begin/begin_generator.rb
38
+ - lib/generators/chapter11_3/begin/instructions.md
39
+ - lib/generators/chapter11_3/begin/templates/spec/controllers/pages_controller_11_3_spec.rb
40
+ - lib/generators/chapter11_3/begin/templates/spec/controllers/microposts_controllers_11_3_spec.rb
41
+ - lib/generators/chapter11_3/solutions/USAGE
42
+ - lib/generators/chapter11_3/solutions/solutions_generator.rb
36
43
  - lib/generators/chapter07/begin/USAGE
37
44
  - lib/generators/chapter07/begin/begin_generator.rb
38
45
  - lib/generators/chapter07/begin/instructions.md
@@ -104,6 +111,17 @@ files:
104
111
  - lib/generators/chapter07/solutions/templates/app/views/layouts/_header.html.erb
105
112
  - lib/generators/chapter07/solutions/templates/app/views/layouts/_footer.html.erb
106
113
  - lib/generators/chapter07/solutions/templates/app/views/users/show.html.erb
114
+ - lib/generators/chapter11_2/begin/USAGE
115
+ - lib/generators/chapter11_2/begin/begin_generator.rb
116
+ - lib/generators/chapter11_2/begin/instructions.md
117
+ - lib/generators/chapter11_2/begin/snippets/custom.css
118
+ - lib/generators/chapter11_2/begin/templates/spec/controllers/users_controller_11_2_spec.rb
119
+ - lib/generators/chapter11_2/begin/templates/app/views/users/show.html.erb
120
+ - lib/generators/chapter11_2/solutions/USAGE
121
+ - lib/generators/chapter11_2/solutions/solutions_generator.rb
122
+ - lib/generators/chapter11_2/solutions/templates/app/controllers/users_controller.rb
123
+ - lib/generators/chapter11_2/solutions/templates/app/views/microposts/_micropost.html.erb
124
+ - lib/generators/chapter11_2/solutions/templates/app/views/users/show.html.erb
107
125
  - lib/generators/chapter10/begin/USAGE
108
126
  - lib/generators/chapter10/begin/begin_generator.rb
109
127
  - lib/generators/chapter10/begin/instructions.md
@@ -138,11 +156,18 @@ files:
138
156
  - lib/generators/chapter08_09/solutions/templates/app/views/devise/passwords/new.html.erb
139
157
  - lib/generators/chapter08_09/solutions/templates/app/views/devise/passwords/edit.html.erb
140
158
  - lib/generators/chapter08_09/solutions/templates/app/views/devise/sessions/new.html.erb
141
- - lib/generators/chapter11/begin/USAGE
142
- - lib/generators/chapter11/begin/begin_generator.rb
143
- - lib/generators/chapter11/begin/instructions.md
144
- - lib/generators/chapter11/solutions/USAGE
145
- - lib/generators/chapter11/solutions/solutions_generator.rb
159
+ - lib/generators/chapter11_1/begin/USAGE
160
+ - lib/generators/chapter11_1/begin/begin_generator.rb
161
+ - lib/generators/chapter11_1/begin/instructions.md
162
+ - lib/generators/chapter11_1/begin/templates/lib/tasks/same_data.rake
163
+ - lib/generators/chapter11_1/begin/templates/spec/models/user_11_1_spec.rb
164
+ - lib/generators/chapter11_1/begin/templates/spec/models/microposts_11_1_spec.rb
165
+ - lib/generators/chapter11_1/solutions/USAGE
166
+ - lib/generators/chapter11_1/solutions/solutions_generator.rb
167
+ - lib/generators/chapter11_1/solutions/snippets/migration_create_microposts.rb
168
+ - lib/generators/chapter11_1/solutions/templates/spec/factories.rb
169
+ - lib/generators/chapter11_1/solutions/templates/app/model/user.rb
170
+ - lib/generators/chapter11_1/solutions/templates/app/model/micropost.rb
146
171
  - LICENSE
147
172
  - Rakefile
148
173
  - Gemfile
@@ -1,2 +0,0 @@
1
- RailsTutorial Chapter 11 Test-First Teaching Instructions
2
- =========================================================