tft_rails 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/generators/chapter08_09/begin/instructions.md +6 -2
- data/lib/generators/chapter10/begin/instructions.md +11 -1
- data/lib/generators/chapter10/begin/templates/spec/controllers/users_controller_10_spec.rb +10 -1
- data/lib/generators/chapter11_1/begin/USAGE +24 -0
- data/lib/generators/{chapter11 → chapter11_1}/begin/begin_generator.rb +6 -2
- data/lib/generators/chapter11_1/begin/instructions.md +39 -0
- data/lib/generators/chapter11_1/begin/templates/lib/tasks/same_data.rake +29 -0
- data/lib/generators/chapter11_1/begin/templates/spec/models/microposts_11_1_spec.rb +45 -0
- data/lib/generators/chapter11_1/begin/templates/spec/models/user_11_1_spec.rb +30 -0
- data/lib/generators/chapter11_1/solutions/USAGE +18 -0
- data/lib/generators/chapter11_1/solutions/snippets/migration_create_microposts.rb +17 -0
- data/lib/generators/chapter11_1/solutions/solutions_generator.rb +25 -0
- data/lib/generators/chapter11_1/solutions/templates/app/model/micropost.rb +10 -0
- data/lib/generators/chapter11_1/solutions/templates/app/model/user.rb +14 -0
- data/lib/generators/chapter11_1/solutions/templates/spec/factories.rb +16 -0
- data/lib/generators/chapter11_2/begin/USAGE +24 -0
- data/lib/generators/chapter11_2/begin/begin_generator.rb +30 -0
- data/lib/generators/chapter11_2/begin/instructions.md +35 -0
- data/lib/generators/chapter11_2/begin/snippets/custom.css +59 -0
- data/lib/generators/chapter11_2/begin/templates/app/views/users/show.html.erb +23 -0
- data/lib/generators/chapter11_2/begin/templates/spec/controllers/users_controller_11_2_spec.rb +31 -0
- data/lib/generators/chapter11_2/solutions/USAGE +18 -0
- data/lib/generators/{chapter11 → chapter11_2}/solutions/solutions_generator.rb +1 -1
- data/lib/generators/chapter11_2/solutions/templates/app/controllers/users_controller.rb +29 -0
- data/lib/generators/chapter11_2/solutions/templates/app/views/microposts/_micropost.html.erb +8 -0
- data/lib/generators/chapter11_2/solutions/templates/app/views/users/show.html.erb +23 -0
- data/lib/generators/chapter11_3/begin/begin_generator.rb +24 -0
- data/lib/generators/chapter11_3/begin/instructions.md +80 -0
- data/lib/generators/chapter11_3/begin/templates/spec/controllers/microposts_controllers_11_3_spec.rb +102 -0
- data/lib/generators/chapter11_3/begin/templates/spec/controllers/pages_controller_11_3_spec.rb +14 -0
- data/lib/generators/chapter11_3/solutions/solutions_generator.rb +8 -0
- metadata +32 -7
- data/lib/generators/chapter11/begin/instructions.md +0 -2
- /data/lib/generators/{chapter11 → chapter11_3}/begin/USAGE +0 -0
- /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]
|
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]
|
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
|
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
|
-
|
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
|
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','
|
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,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>
|
data/lib/generators/chapter11_2/begin/templates/spec/controllers/users_controller_11_2_spec.rb
ADDED
@@ -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"
|
@@ -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,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"
|
data/lib/generators/chapter11_3/begin/templates/spec/controllers/microposts_controllers_11_3_spec.rb
ADDED
@@ -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
|
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.
|
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-
|
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/
|
142
|
-
- lib/generators/
|
143
|
-
- lib/generators/
|
144
|
-
- lib/generators/
|
145
|
-
- lib/generators/
|
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
|
File without changes
|
File without changes
|