diff --git a/kaku/sample_app/!! b/kaku/sample_app/!! new file mode 100644 index 0000000..f227aab --- /dev/null +++ b/kaku/sample_app/!! @@ -0,0 +1,6 @@ +
  • +<%= micropost.content %> + + Posted <%= time_ago_in_words(micropost.created_at) %> ago. + +
  • diff --git a/kaku/sample_app/Gemfile b/kaku/sample_app/Gemfile index 81dd1a6..3eb9bf0 100644 --- a/kaku/sample_app/Gemfile +++ b/kaku/sample_app/Gemfile @@ -4,6 +4,9 @@ ruby '2.0.0' gem 'rails', '4.0.0' gem 'bootstrap-sass', '2.3.2.0' gem 'bcrypt-ruby', '3.0.1' +gem 'faker', '1.1.2' +gem 'will_paginate', '3.0.4' +gem 'bootstrap-will_paginate', '0.0.9' group :development, :test do gem 'sqlite3', '1.3.7' gem 'rspec-rails', '2.13.1' @@ -13,6 +16,7 @@ group :development, :test do end group :test do + gem 'webrat' gem 'selenium-webdriver', '2.0.0' gem 'capybara', '2.1.0' gem 'guard-rspec','2.5.0' diff --git a/kaku/sample_app/Gemfile.lock b/kaku/sample_app/Gemfile.lock index 51d2f54..8baa423 100644 --- a/kaku/sample_app/Gemfile.lock +++ b/kaku/sample_app/Gemfile.lock @@ -37,6 +37,8 @@ GEM bcrypt-ruby (3.0.1) bootstrap-sass (2.3.2.0) sass (~> 3.2) + bootstrap-will_paginate (0.0.9) + will_paginate builder (3.1.4) capybara (2.1.0) mime-types (>= 1.16) @@ -63,6 +65,8 @@ GEM factory_girl_rails (4.2.1) factory_girl (~> 4.2.0) railties (>= 3.0.0) + faker (1.1.2) + i18n (~> 0.5) ffi (1.9.0) formatador (0.2.4) guard (1.8.1) @@ -186,6 +190,11 @@ GEM uglifier (2.1.1) execjs (>= 0.3.0) multi_json (~> 1.0, >= 1.0.2) + webrat (0.7.3) + nokogiri (>= 1.2.0) + rack (>= 1.0) + rack-test (>= 0.5.3) + will_paginate (3.0.4) xpath (2.0.0) nokogiri (~> 1.3) @@ -195,10 +204,12 @@ PLATFORMS DEPENDENCIES bcrypt-ruby (= 3.0.1) bootstrap-sass (= 2.3.2.0) + bootstrap-will_paginate (= 0.0.9) capybara (= 2.1.0) childprocess (= 0.3.6) coffee-rails (= 4.0.0) factory_girl_rails (= 4.2.1) + faker (= 1.1.2) guard-rspec (= 2.5.0) guard-spork (= 1.5.0) jbuilder (= 1.0.2) @@ -213,3 +224,5 @@ DEPENDENCIES sqlite3 (= 1.3.7) turbolinks (= 1.1.1) uglifier (= 2.1.1) + webrat + will_paginate (= 3.0.4) diff --git a/kaku/sample_app/app/assets/javascripts/application.js b/kaku/sample_app/app/assets/javascripts/application.js index d6925fa..e3a9f2d 100644 --- a/kaku/sample_app/app/assets/javascripts/application.js +++ b/kaku/sample_app/app/assets/javascripts/application.js @@ -12,5 +12,6 @@ // //= require jquery //= require jquery_ujs +//= require bootstrap //= require turbolinks //= require_tree . diff --git a/kaku/sample_app/app/assets/javascripts/sessions.js.coffee b/kaku/sample_app/app/assets/javascripts/sessions.js.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/kaku/sample_app/app/assets/javascripts/sessions.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/kaku/sample_app/app/assets/stylesheets/custom.css.scss b/kaku/sample_app/app/assets/stylesheets/custom.css.scss index a65aba3..bf968eb 100644 --- a/kaku/sample_app/app/assets/stylesheets/custom.css.scss +++ b/kaku/sample_app/app/assets/stylesheets/custom.css.scss @@ -134,4 +134,58 @@ input, textarea, select, .uneditable-input { input { height: auto !important; -} \ No newline at end of file +} + +#error_explanation { + color: #f00; + ul { + list-style: none; + margin: 0 0 18px 0; + } +} + +.field_with_errors { + @extend .control-group; + @extend .error; + } + +.users { + list-style: none; + margin: 0; + li { + overflow: auto; + padding: 10px 0; + border-top: 1px solid $grayLighter; + &:last-child { + border-bottom: 1px solid $grayLighter; + } + } +} + +/* microposts */ + +.microposts { + list-style: none; + margin: 10px 0 0 0; + + li { + padding: 10px 0; + border-top: 1px solid #e8e8e8; + } +} +.content { + display: block; +} +.timestamp { + color: $grayLight; +} +.gravatar { + float: left; + margin-right: 10px; +} +aside { + textarea { + height: 100px; + margin-bottom: 5px; + } +} diff --git a/kaku/sample_app/app/assets/stylesheets/sessions.css.scss b/kaku/sample_app/app/assets/stylesheets/sessions.css.scss new file mode 100644 index 0000000..ccb1ed2 --- /dev/null +++ b/kaku/sample_app/app/assets/stylesheets/sessions.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Sessions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/kaku/sample_app/app/controllers/.users_controller.rb.swp b/kaku/sample_app/app/controllers/.users_controller.rb.swp new file mode 100644 index 0000000..6ffa31f Binary files /dev/null and b/kaku/sample_app/app/controllers/.users_controller.rb.swp differ diff --git a/kaku/sample_app/app/controllers/application_controller.rb b/kaku/sample_app/app/controllers/application_controller.rb index d83690e..d8b3d09 100644 --- a/kaku/sample_app/app/controllers/application_controller.rb +++ b/kaku/sample_app/app/controllers/application_controller.rb @@ -2,4 +2,5 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + include SessionsHelper end diff --git a/kaku/sample_app/app/controllers/microposts_controller.rb b/kaku/sample_app/app/controllers/microposts_controller.rb new file mode 100644 index 0000000..22580dd --- /dev/null +++ b/kaku/sample_app/app/controllers/microposts_controller.rb @@ -0,0 +1,31 @@ +class MicropostsController < ApplicationController + before_action :signed_in_user + before_action :correct_user, only: :destroy + + def create + @micropost = current_user.microposts.build(micropost_params) + if @micropost.save + flash[:success] = "Micropost created!" + redirect_to root_url + else + @feed_items = [] + render 'static_pages/home' + end + end + + def destroy + @micropost.destroy + redirect_to root_url + end + + private + + def micropost_params + params.require(:micropost).permit(:content) + end + def correct_user + @micropost = current_user.microposts.find_by(id: params[:id]) + redirect_to root_url if @micropost.nil? + end +end + diff --git a/kaku/sample_app/app/controllers/sessions_controller.rb b/kaku/sample_app/app/controllers/sessions_controller.rb new file mode 100644 index 0000000..a6ad58d --- /dev/null +++ b/kaku/sample_app/app/controllers/sessions_controller.rb @@ -0,0 +1,21 @@ +class SessionsController < ApplicationController + def new + end + + def create + user = User.find_by(email: params[:session][:email].downcase) + if user && user.authenticate(params[:session][:password]) + # Sign the user in and redirect to the user's show page. + sign_in user + redirect_to user + else + flash.now[:error] = 'Invalid email/password combination' + render 'new' + end + end + + def destroy + sign_out + redirect_to root_path + end +end diff --git a/kaku/sample_app/app/controllers/static_pages_controller.rb b/kaku/sample_app/app/controllers/static_pages_controller.rb index 321bb35..4f35253 100644 --- a/kaku/sample_app/app/controllers/static_pages_controller.rb +++ b/kaku/sample_app/app/controllers/static_pages_controller.rb @@ -1,5 +1,9 @@ class StaticPagesController < ApplicationController def home + if signed_in? + @micropost = current_user.microposts.build + @feed_items = current_user.feed.paginate(page: params[:page]) + end end def help diff --git a/kaku/sample_app/app/controllers/users_controller.rb b/kaku/sample_app/app/controllers/users_controller.rb index 9e15c6b..9156022 100644 --- a/kaku/sample_app/app/controllers/users_controller.rb +++ b/kaku/sample_app/app/controllers/users_controller.rb @@ -1,23 +1,75 @@ class UsersController < ApplicationController + before_action :currect_user,only: [:edit, :update] + before_action :signed_in_user,only: [:index, :edit, :update] + before_action :admin_user, only: :destroy + def new @user = User.new end - + def show - @user = User.find(params[:id]) + @user = User.find(params[:id]) + @microposts = @user.microposts.paginate(page: params[:page]) end - + def create - @user = User.new(params[:user]) # Not the final implementation! + @user = User.new(user_params) # Not the final implementation! if @user.save - # Handle a successful save. + sign_in @user + flash[:success] = "Welcome to the Sample App!" + redirect_to @user else render 'new' end end - + + def destroy + User.find(params[:id]).destroy + flash[:success] = "User destroyed." + redirect_to users_url + end + + def sign_out + self.current_user = nil + cookies.delete(:remember_token) + end + + def edit + @user = User.find(params[:id]) + end + + def update + @user = User.find(params[:id]) + if @user.update_attributes(user_params) + flash[:success] = "Profile updated" + sign_in @user + redirect_to @user + else + render 'edit' + + end + end + + def index + @users = User.paginate(page: params[:page]) + end + private - def user_params - params.require(:user).permit(:name, :email, :password, :password_confirmation) - end + def user_params + params.require(:user).permit(:name, :email, :password, :password_confirmation) + end + + def signed_in_user + redirect_to signin_url, notice: "Please sign in." unless signed_in? + + end + + def currect_user + @user = User.find(params[:id]) + redirect_to(root_path) unless current_user?(@user) + end + + def admin_user + redirect_to(root_path) unless current_user.admin? + end end diff --git a/kaku/sample_app/app/helpers/sessions_helper.rb b/kaku/sample_app/app/helpers/sessions_helper.rb new file mode 100644 index 0000000..a2343da --- /dev/null +++ b/kaku/sample_app/app/helpers/sessions_helper.rb @@ -0,0 +1,61 @@ +module SessionsHelper + def sign_in(user) + remember_token = User.new_remember_token + cookies.permanent[:remember_token] = remember_token + user.update_attribute(:remember_token, User.encrypt(remember_token)) + self.current_user = user + end + + def signed_in_user + unless signed_in? + store_location + redirect_to signin_url, notice: "Please sign in." + end + end + + def signed_in? + !current_user.nil? + end + + def current_user=(user) + @current_user = user + end + + def current_user + remember_token = User.encrypt(cookies[:remember_token]) + @current_user ||= User.find_by(remember_token: remember_token) + end + + def current_user?(user) + user == current_user + end + + def sign_out + self.current_user = nil + cookies.delete(:remember_token) + end + + def redirect_back_or(default) + redirect_to(seeesion[:return_to] || default) + session.delete(:return_to) + end + + def store_location + session[:return_to] = request.fullpath + end + + def create + user = User.find_by(email: params[:session][:email].downcase) + if user && user.authenticate(session_params) + sign_in user + redirect_back_or user + else + flash.now[:error] = 'Invalid email/password combination' + render 'new' + end + end + + def session_params + params.require(:session).permit(:email, :password) + end +end diff --git a/kaku/sample_app/app/models/micropost.rb b/kaku/sample_app/app/models/micropost.rb new file mode 100644 index 0000000..96e9c89 --- /dev/null +++ b/kaku/sample_app/app/models/micropost.rb @@ -0,0 +1,6 @@ +class Micropost < ActiveRecord::Base + belongs_to :user + default_scope -> {order('created_at DESC')} + validates :content, presence: true,length: { maximum: 140 } + validates :user_id, presence: true +end diff --git a/kaku/sample_app/app/models/user.rb b/kaku/sample_app/app/models/user.rb index f30c482..728c695 100644 --- a/kaku/sample_app/app/models/user.rb +++ b/kaku/sample_app/app/models/user.rb @@ -1,8 +1,28 @@ class User < ActiveRecord::Base + has_many :microposts,dependent: :destroy before_save { self.email = email.downcase } + before_create :create_remember_token + validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format:{with: VALID_EMAIL_REGEX}, uniqueness: { case_sensitive: false} validates :password, length: { minimum: 6 } has_secure_password + + def User.new_remember_token + SecureRandom.urlsafe_base64 + end + + def User.encrypt(token) + Digest::SHA1.hexdigest(token.to_s) + end + + def feed + Micropost.where("user_id=?",id) + end + private + + def create_remember_token + self.remember_token = User.encrypt(User.new_remember_token) + end end diff --git a/kaku/sample_app/app/views/layouts/_header.html.erb b/kaku/sample_app/app/views/layouts/_header.html.erb index 2a56299..df44a27 100644 --- a/kaku/sample_app/app/views/layouts/_header.html.erb +++ b/kaku/sample_app/app/views/layouts/_header.html.erb @@ -1,14 +1,31 @@ diff --git a/kaku/sample_app/app/views/layouts/application.html.erb b/kaku/sample_app/app/views/layouts/application.html.erb index 7f6ed7b..1b9ee35 100644 --- a/kaku/sample_app/app/views/layouts/application.html.erb +++ b/kaku/sample_app/app/views/layouts/application.html.erb @@ -11,8 +11,12 @@ <%= render 'layouts/header' %>
    - <%= yield %> - <%= debug(params) if Rails.env.development? %> + <% flash.each do |key, value| %> +
    <%= value %>
    + <% end %> + <%= yield %> + <%= render 'layouts/footer' %> + <%= debug(params) if Rails.env.development? %>
    \ No newline at end of file diff --git a/kaku/sample_app/app/views/microposts/_micropost.html.erb b/kaku/sample_app/app/views/microposts/_micropost.html.erb new file mode 100644 index 0000000..7bdcd3b --- /dev/null +++ b/kaku/sample_app/app/views/microposts/_micropost.html.erb @@ -0,0 +1,11 @@ +
  • +<%= micropost.content %> + + Posted <%= time_ago_in_words(micropost.created_at) %> ago. + +<% if current_user?(micropost.user) %> + <%= link_to "delete", micropost, method: :delete, + data: { confirm: "You sure?" }, + title: micropost.content %> + <% end %> +
  • diff --git a/kaku/sample_app/app/views/sessions/new.html.erb b/kaku/sample_app/app/views/sessions/new.html.erb new file mode 100644 index 0000000..861efb8 --- /dev/null +++ b/kaku/sample_app/app/views/sessions/new.html.erb @@ -0,0 +1,19 @@ +<% provide(:title, "Sign in") %> +

    Sign in

    + +
    +
    + <%= form_for(:session, url: sessions_path) do |f| %> + + <%= f.label :email %> + <%= f.text_field :email %> + + <%= f.label :password %> + <%= f.password_field :password %> + + <%= f.submit "Sign in", class: "btn btn-large btn-primary" %> + <% end %> + +

    New user? <%= link_to "Sign up now!", signup_path %>

    +
    +
    \ No newline at end of file diff --git a/kaku/sample_app/app/views/shared/_error_messages.html.erb b/kaku/sample_app/app/views/shared/_error_messages.html.erb new file mode 100644 index 0000000..dce461f --- /dev/null +++ b/kaku/sample_app/app/views/shared/_error_messages.html.erb @@ -0,0 +1,12 @@ +<% if object.errors.any? %> +
    +
    + The form contains <%= pluralize(object.errors.count, "error") %>. +
    + +
    +<% end %> diff --git a/kaku/sample_app/app/views/shared/_feed.html.erb b/kaku/sample_app/app/views/shared/_feed.html.erb new file mode 100644 index 0000000..d46d8b8 --- /dev/null +++ b/kaku/sample_app/app/views/shared/_feed.html.erb @@ -0,0 +1,6 @@ +<% if @feed_items.any? %> +
      + <%= render partial: 'shared/feed_item', collection: @feed_items %> +
    + <%= will_paginate @feed_items %> +<% end %> diff --git a/kaku/sample_app/app/views/shared/_feed_item.html.erb b/kaku/sample_app/app/views/shared/_feed_item.html.erb new file mode 100644 index 0000000..42e8976 --- /dev/null +++ b/kaku/sample_app/app/views/shared/_feed_item.html.erb @@ -0,0 +1,15 @@ +
  • +<%= link_to gravatar_for(feed_item.user), feed_item.user %> + + <%= link_to feed_item.user.name, feed_item.user %> + +<%= feed_item.content %> + + Posted <%= time_ago_in_words(feed_item.created_at) %> ago. + +<% if current_user?(feed_item.user) %> + <%= link_to "delete", feed_item, method: :delete, + data: { confirm: "You sure?" }, + title: feed_item.content %> + <% end %> +
  • diff --git a/kaku/sample_app/app/views/shared/_micropost_form.html.erb b/kaku/sample_app/app/views/shared/_micropost_form.html.erb new file mode 100644 index 0000000..e2c2bf8 --- /dev/null +++ b/kaku/sample_app/app/views/shared/_micropost_form.html.erb @@ -0,0 +1,7 @@ +<%= form_for(@micropost) do |f| %> + <%= render 'shared/error_messages', object: f.object %> +
    + <%= f.text_area :content, placeholder: "Compose new micropost..." %> +
    + <%= f.submit "Post", class: "btn btn-large btn-primary" %> +<% end %> diff --git a/kaku/sample_app/app/views/shared/_user_info.html.erb b/kaku/sample_app/app/views/shared/_user_info.html.erb new file mode 100644 index 0000000..607fc9d --- /dev/null +++ b/kaku/sample_app/app/views/shared/_user_info.html.erb @@ -0,0 +1,12 @@ + + <%= gravatar_for current_user %> + +

    + <%= current_user.name %> +

    + + <%= link_to "view my profile", current_user %> + + + <%= pluralize(current_user.microposts.count, "micropost") %> + diff --git a/kaku/sample_app/app/views/static_pages/home.html.erb b/kaku/sample_app/app/views/static_pages/home.html.erb index 747da08..926f91e 100644 --- a/kaku/sample_app/app/views/static_pages/home.html.erb +++ b/kaku/sample_app/app/views/static_pages/home.html.erb @@ -1,16 +1,32 @@ -<% provide(:title, '') %> +<% if signed_in? %> +
    + +
    +

    Micropost Feed

    + <%= render 'shared/feed' %> +
    +
    + +<% else %> +
    +

    Welcome to the Sample App

    -
    - -

    Welcome to the Sample App

    +

    + This is the home page for the + Ruby on Rails Tutorial + sample application. +

    -

    - This is the home page for the - Ruby on Rails Tutorial - sample application. -

    + <%= link_to "Sign up now!", signup_path, + class: "btn btn-large btn-primary" %> +
    - <%= link_to "Sign up now!", signup_path, class: "btn btn-large btn-primary" %> -
    - -<%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %> + <%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %> + <% end %> diff --git a/kaku/sample_app/app/views/users/._user.html.erb.swo b/kaku/sample_app/app/views/users/._user.html.erb.swo new file mode 100644 index 0000000..a3f51d4 Binary files /dev/null and b/kaku/sample_app/app/views/users/._user.html.erb.swo differ diff --git a/kaku/sample_app/app/views/users/._user.html.erb.swp b/kaku/sample_app/app/views/users/._user.html.erb.swp new file mode 100644 index 0000000..10a3efb Binary files /dev/null and b/kaku/sample_app/app/views/users/._user.html.erb.swp differ diff --git a/kaku/sample_app/app/views/users/.index.html.erb.swo b/kaku/sample_app/app/views/users/.index.html.erb.swo new file mode 100644 index 0000000..1721950 Binary files /dev/null and b/kaku/sample_app/app/views/users/.index.html.erb.swo differ diff --git a/kaku/sample_app/app/views/users/_user.html.erb b/kaku/sample_app/app/views/users/_user.html.erb new file mode 100644 index 0000000..505a7b8 --- /dev/null +++ b/kaku/sample_app/app/views/users/_user.html.erb @@ -0,0 +1,8 @@ +
  • +<%= gravatar_for user %> +<%= link_to user.name, user %> +<% if current_user.admin? && !current_user?(user) %> + | <%= link_to "delete", user, method: :delete, + data: { confirm: "You sure?" } %> +<% end %> +
  • diff --git a/kaku/sample_app/app/views/users/edit.html.erb b/kaku/sample_app/app/views/users/edit.html.erb new file mode 100644 index 0000000..bf14949 --- /dev/null +++ b/kaku/sample_app/app/views/users/edit.html.erb @@ -0,0 +1,28 @@ +<% provide(:title, "Edit user") %> +

    Update your profile

    + +
    + +
    + <%= form_for(@user) do |f| %> + <%= render 'shared/error_messages', object: f.object %> + + <%= f.label :name %> + <%= f.text_field :name %> + + <%= f.label :email %> + <%= f.text_field :email %> + + <%= f.label :password %> + <%= f.password_field :password %> + + <%= f.label :password_confirmation, "Confirm Password" %> + <%= f.password_field :password_confirmation %> + + <%= f.submit "Save changes", class: "btn btn-large btn-primary" %> + <% end %> + + <%= gravatar_for @user %> + change +
    +
    diff --git a/kaku/sample_app/app/views/users/index.html.erb b/kaku/sample_app/app/views/users/index.html.erb new file mode 100644 index 0000000..aa43af0 --- /dev/null +++ b/kaku/sample_app/app/views/users/index.html.erb @@ -0,0 +1,7 @@ +<% provide(:title, 'All users') %> +

    All users

    +<%= will_paginate %> + +<%= will_paginate %> diff --git a/kaku/sample_app/app/views/users/new.html.erb b/kaku/sample_app/app/views/users/new.html.erb index 8a2bbf3..545279f 100644 --- a/kaku/sample_app/app/views/users/new.html.erb +++ b/kaku/sample_app/app/views/users/new.html.erb @@ -4,7 +4,7 @@
    <%= form_for(@user) do |f| %> - <%= render 'shared/error_messages' %> + <%= render 'shared/error_messages',object: f.object %> <%= f.label :name %> <%= f.text_field :name %> @@ -20,4 +20,4 @@ <%= f.submit "Create my account", class: "btn btn-large btn-primary" %> <% end %>
    -
    \ No newline at end of file + diff --git a/kaku/sample_app/app/views/users/show.html.erb b/kaku/sample_app/app/views/users/show.html.erb index a6db57a..692b11d 100644 --- a/kaku/sample_app/app/views/users/show.html.erb +++ b/kaku/sample_app/app/views/users/show.html.erb @@ -1,11 +1,20 @@ <% provide(:title, @user.name) %>
    -
    \ No newline at end of file +
    + <% if @user.microposts.any? %> +

    Microposts (<%= @user.microposts.count %>)

    +
      + <%= render @microposts %> +
    + <%= will_paginate @microposts %> + <% end %> +
    + diff --git a/kaku/sample_app/config/environments/production.rb b/kaku/sample_app/config/environments/production.rb index 5d0ee57..f112db5 100644 --- a/kaku/sample_app/config/environments/production.rb +++ b/kaku/sample_app/config/environments/production.rb @@ -40,7 +40,7 @@ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true + config.force_ssl = true # Set to :debug to see everything in the log. config.log_level = :info diff --git a/kaku/sample_app/config/routes.rb b/kaku/sample_app/config/routes.rb index a87161d..064c942 100644 --- a/kaku/sample_app/config/routes.rb +++ b/kaku/sample_app/config/routes.rb @@ -1,12 +1,15 @@ SampleApp::Application.routes.draw do resources :users root to: 'static_pages#home' + resources :sessions, only: [:new, :create, :destroy] + resources :microposts, only: [:create, :destroy] match '/signup', to: 'users#new', via: 'get' + match '/signin', to: 'sessions#new', via: 'get' + match '/signout', to: 'sessions#destroy', via: 'delete' match '/help', to: 'static_pages#help', via: 'get' match '/about', to: 'static_pages#about', via: 'get' match '/contact', to: 'static_pages#contact', via: 'get' - - # The priority is based upon order of creation: first created -> highest priority. + # The priority is based upon order of creation: first created -> highest priority. # # # See how all your routes lay out with "rake routes". diff --git a/kaku/sample_app/db/migrate/20130813135754_add_remember_token_to_users.rb b/kaku/sample_app/db/migrate/20130813135754_add_remember_token_to_users.rb new file mode 100644 index 0000000..74c254f --- /dev/null +++ b/kaku/sample_app/db/migrate/20130813135754_add_remember_token_to_users.rb @@ -0,0 +1,4 @@ +class AddRememberTokenToUsers < ActiveRecord::Migration + def change + end +end diff --git a/kaku/sample_app/db/migrate/20130815143550_add_remember_token_to_users.rb b/kaku/sample_app/db/migrate/20130815143550_add_remember_token_to_users.rb new file mode 100644 index 0000000..8a62dcd --- /dev/null +++ b/kaku/sample_app/db/migrate/20130815143550_add_remember_token_to_users.rb @@ -0,0 +1,6 @@ +class AddRememberTokenToUsers < ActiveRecord::Migration + def change + add_column :users, :remember_token, :string + add_index :users, :remember_token + end +end diff --git a/kaku/sample_app/db/migrate/20130826130430_add_admin_to_users.rb b/kaku/sample_app/db/migrate/20130826130430_add_admin_to_users.rb new file mode 100644 index 0000000..2ddf290 --- /dev/null +++ b/kaku/sample_app/db/migrate/20130826130430_add_admin_to_users.rb @@ -0,0 +1,5 @@ +class AddAdminToUsers < ActiveRecord::Migration + def change + add_column :users, :admin, :boolean + end +end diff --git a/kaku/sample_app/db/migrate/20130826143009_create_microposts.rb b/kaku/sample_app/db/migrate/20130826143009_create_microposts.rb new file mode 100644 index 0000000..1ea9615 --- /dev/null +++ b/kaku/sample_app/db/migrate/20130826143009_create_microposts.rb @@ -0,0 +1,10 @@ +class CreateMicroposts < ActiveRecord::Migration + def change + create_table :microposts do |t| + t.string :content + t.integer :user_id + + t.timestamps + end + end +end diff --git a/kaku/sample_app/db/schema.rb b/kaku/sample_app/db/schema.rb index 1644516..a7946eb 100644 --- a/kaku/sample_app/db/schema.rb +++ b/kaku/sample_app/db/schema.rb @@ -11,7 +11,14 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20130730151702) do +ActiveRecord::Schema.define(version: 20130826143009) do + + create_table "microposts", force: true do |t| + t.string "content" + t.integer "user_id" + t.datetime "created_at" + t.datetime "updated_at" + end create_table "users", force: true do |t| t.string "name" @@ -19,8 +26,11 @@ t.datetime "created_at" t.datetime "updated_at" t.string "password_digest" + t.string "remember_token" + t.boolean "admin" end add_index "users", ["email"], name: "index_users_on_email", unique: true + add_index "users", ["remember_token"], name: "index_users_on_remember_token" end diff --git a/kaku/sample_app/lib/tasks/sample_data.rake b/kaku/sample_app/lib/tasks/sample_data.rake new file mode 100644 index 0000000..fec3c51 --- /dev/null +++ b/kaku/sample_app/lib/tasks/sample_data.rake @@ -0,0 +1,26 @@ +namespace :db do + desc "Fill database with sample data" + task populate: :environment do + + admin = User.create!(name: "Example User", + email: "example@railstutorial.org", + password: "foobar", + password_confirmation: "foobar", + admin: true) + 99.times do |n| + name = Faker::Name.name + email = "example-#{n+1}@railstutorial.org" + password = "password" + User.create!(name: name, + email: email, + password: password, + password_confirmation: password) + end + + users = User.all(limit: 6) + 50.times do + content = Faker::Lorem.sentence(5) + users.each { |user| user.microposts.create!(content: content) } + end + end +end diff --git a/kaku/sample_app/spec/factories.rb b/kaku/sample_app/spec/factories.rb index 2b4e8d3..4b0dc0f 100644 --- a/kaku/sample_app/spec/factories.rb +++ b/kaku/sample_app/spec/factories.rb @@ -1,8 +1,16 @@ FactoryGirl.define do factory :user do - name "Michael Hartl" - email "michael@example.com" + sequence(:name) { |n| "Person #{n}" } + sequence(:email) { |n| "person_#{n}@example.com"} password "foobar" password_confirmation "foobar" + factory :admin do + admin true + end end -end \ No newline at end of file + + factory :micropost do + content "Lorem ipsum" + user + end +end diff --git a/kaku/sample_app/spec/models/.user_spec.rb.swn b/kaku/sample_app/spec/models/.user_spec.rb.swn new file mode 100644 index 0000000..821db5e Binary files /dev/null and b/kaku/sample_app/spec/models/.user_spec.rb.swn differ diff --git a/kaku/sample_app/spec/models/micropost_spec.rb b/kaku/sample_app/spec/models/micropost_spec.rb new file mode 100644 index 0000000..72197cc --- /dev/null +++ b/kaku/sample_app/spec/models/micropost_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe Micropost do + let(:user) {FactoryGirl.create(:user)} + before do + @micropost = user.microposts.build(content: "Lorem ipsum") + end + + subject {@micropost} + + it { should respond_to(:content)} + it { should respond_to(:user_id)} + it { should respond_to(:user)} + its(:user) { should eq user} + + it { should be_valid} + + describe "when user_id is not present" do + before { @micropost.user_id = nil } + it { should_not be_valid} + end + + describe "when user_id is not present" do + before { @micropost.user_id = nil } + it { should_not be_valid } + end + + describe "with blank content" do + before { @micropost.content = " " } + it { should_not be_valid } + end + + describe "with content that is too long" do + before { @micropost.content = "a" * 141 } + it { should_not be_valid } + end +end diff --git a/kaku/sample_app/spec/models/user_spec.rb b/kaku/sample_app/spec/models/user_spec.rb index c764283..39f8a70 100644 --- a/kaku/sample_app/spec/models/user_spec.rb +++ b/kaku/sample_app/spec/models/user_spec.rb @@ -2,20 +2,23 @@ describe User do before { @user = User.new(name: "Example User", - email: "ghjcumt2008@gmail.com", - password: "foobar", - password_confirmation: "foobar") + email: "ghjcumt2008@gmail.com", + password: "foobar", + password_confirmation: "foobar") } subject { @user } - + it{ should respond_to(:name)} it{ should respond_to(:email)} it { should respond_to(:password_digest) } it { should respond_to(:password) } it { should respond_to(:password_confirmation) } + it { should respond_to(:remember_token) } it { should respond_to(:authenticate) } - + it { should respond_to(:admin)} + it { should respond_to(:microposts)} + describe "when name is not presence" do before{ @user.name = ""} it{ should_not be_valid} @@ -60,7 +63,7 @@ it{ should_not be_valid} end - + describe "return value of authenticate method" do before { @user.save } let(:found_user) { User.find_by(email: @user.email) } @@ -75,12 +78,49 @@ it { should_not eq user_for_invalid_password } specify { expect(user_for_invalid_password).to be_false } end - + describe "with a password that's too short" do before { @user.password = @user.password_confirmation = "a" * 5 } it { should be_invalid } end end - -end + describe "remember token" do + before { @user.save } + its(:remember_token) { should_not be_blank } + end + + describe "micropost associations" do + before { @user.save } + let!(:older_micropost) do + FactoryGirl.create(:micropost, user: @user, created_at: 1.day.age) + end + let!(:newer_micropost) do + FactoryGirl.create(:micropost, user: @user, created_at: 1.hour.ago) + end + + it "should have the right microposts in the right order" do + expect(@user.microposts.to_a).to eq [newer_micropost, older_micropost] + end + + it "should destroy associated microposts" do + microposts = @user.microposts.to_a + @user.destroy + expect(microposts).not_to be_empty + microposts.each do |micropost| + expect(Micropost.where(id: micropost.id)).to be_empty + end + end + end + + describe "status" do + let(:unfollowed_post) do + FactoryGirl.create(:micropost, user: FactoryGirl.create(:user)) + end + + its(:feed) { should include(newer_micropost) } + its(:feed) { should include(older_micropost) } + its(:feed) { should_not include(unfollowed_post) } + end + +end diff --git a/kaku/sample_app/spec/requests/.authentication_pages_spec.rb.swp b/kaku/sample_app/spec/requests/.authentication_pages_spec.rb.swp new file mode 100644 index 0000000..c5e74e2 Binary files /dev/null and b/kaku/sample_app/spec/requests/.authentication_pages_spec.rb.swp differ diff --git a/kaku/sample_app/spec/requests/authentication_pages_spec.rb b/kaku/sample_app/spec/requests/authentication_pages_spec.rb new file mode 100644 index 0000000..5d7e881 --- /dev/null +++ b/kaku/sample_app/spec/requests/authentication_pages_spec.rb @@ -0,0 +1,118 @@ +describe "Authentication" do + + subject { page } + + describe "signin" do + before { visit signin_path } + + describe "with invalid information" do + before { click_button "Sign in" } + + it { should have_title('Sign in') } + it { should have_selector('div.alert.alert-error', text: 'Invalid email/password combination') } + end + + describe "with valid information" do + let(:user) { FactoryGirl.create(:user) } + before do + fill_in "Email", with: user.email.upcase + fill_in "Password", with: user.password + click_button "Sign in" + end + + it { should have_title(user.name) } + it { should have_link('Users', href: users_path) } + it { should have_link('Profile', href: user_path(user)) } + it { should have_link('Settings', href: edit_user_path(user)) } + it { should have_link('Sign out', href: signout_path) } + it { should_not have_link('Sign in', href: signin_path) } + end + + end + + describe "authorization" do + + describe "for non-signed-in users" do + let(:user) { FactoryGirl.create(:user) } + + describe "in the Users controller" do + describe "visiting the user index" do + before { visit users_path } + it { should have_title('Sign in') } + end + + describe "visiting the edit page" do + before { visit edit_user_path(user) } + it { should have_title('Sign in') } + end + + describe "submitting to the update action" do + before { patch user_path(user) } + specify { expect(response).to redirect_to(signin_path) } + end + end + + describe "as non-admin user" do + let(:user) { FactoryGirl.create(:user) } + let(:non_admin) { FactoryGirl.create(:user) } + + before { sign_in non_admin, no_capybara: true } + + describe "submitting a DELETE request to the Users#destroy action" do + before { delete user_path(user) } + specify { expect(response).to redirect_to(root_path) } + end + end + end + end + + describe "in the Microposts controller" do + + describe "submitting to the create action" do + before { post microposts_path } + specify { expect(response).to redirect_to(signin_path) } + end + + describe "submitting to the destroy action" do + before { delete micropost_path(FactoryGirl.create(:micropost)) } + specify { expect(response).to redirect_to(signin_path) } + end + end + + describe "as wrong user" do + let(:user) { FactoryGirl.create(:user) } + let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") } + before { sign_in user, no_capybara: true } + + describe "visiting Users#edit page" do + before { visit edit_user_path(wrong_user) } + it { should_not have_title(full_title('Edit user')) } + end + + describe "submitting a PATCH request to the Users#update action" do + before { patch user_path(wrong_user) } + specify { expect(response).to redirect_to(root_path) } + end + end + + describe "for non-signed-in users" do + let(:user) { FactoryGirl.create(:user) } + + describe "when attempting to visit a protected page" do + before do + visit edit_user_path(user) + fill_in "Email", with: user.email + fill_in "Password", with: user.password + click_button "Sign in" + end + + describe "after signing in" do + + it "should render the desired protected page" do + expect(page).to have_title('Edit user') + end + end + end + end + +end diff --git a/kaku/sample_app/spec/requests/micropost_pages_spec.rb b/kaku/sample_app/spec/requests/micropost_pages_spec.rb new file mode 100644 index 0000000..842d706 --- /dev/null +++ b/kaku/sample_app/spec/requests/micropost_pages_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe "Micropost pages" do + + subject { page } + + let(:user) { FactoryGirl.create(:user) } + before { sign_in user } + + describe "micropost creation" do + before { visit root_path } + + describe "with invalid information" do + + it "should not create a micropost" do + expect { click_button "Post" }.not_to change(Micropost, :count) + end + + describe "error messages" do + before { click_button "Post" } + it { should have_content('error') } + end + end + + describe "with valid information" do + + before { fill_in 'micropost_content', with: "Lorem ipsum" } + it "should create a micropost" do + expect { click_button "Post" }.to change(Micropost, :count).by(1) + end + end + end +end diff --git a/kaku/sample_app/spec/requests/static_pages_spec.rb b/kaku/sample_app/spec/requests/static_pages_spec.rb index 70f0ee1..1384305 100644 --- a/kaku/sample_app/spec/requests/static_pages_spec.rb +++ b/kaku/sample_app/spec/requests/static_pages_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +# require 'spec_helper' describe "Static pages" do @@ -32,4 +32,20 @@ it { should have_content('Contact') } it { should have_title(full_title('Contact')) } end + + describe "for signed-in users" do + let(:user) { FactoryGirl.create(:user) } + before do + FactoryGirl.create(:micropost, user: user, content: "Lorem ipsum") + FactoryGirl.create(:micropost, user: user, content: "Dolor sit amet") + sign_in user + visit root_path + end + + it "should render the user's feed" do + user.feed.each do |item| + expect(page).to have_selector("li##{item.id}", text: item.content) + end + end + end end diff --git a/kaku/sample_app/spec/requests/user_pages_spec.rb b/kaku/sample_app/spec/requests/user_pages_spec.rb index df9c173..1b691df 100644 --- a/kaku/sample_app/spec/requests/user_pages_spec.rb +++ b/kaku/sample_app/spec/requests/user_pages_spec.rb @@ -4,13 +4,59 @@ subject { page } + describe "index" do + let(:user) { FactoryGirl.create(:user) } + before(:each) do + sign_in user + visit users_path + end + + it { should have_title('All users') } + it { should have_content('All users') } + + describe "pagination" do + + before(:all) { 30.times { FactoryGirl.create(:user) } } + after(:all) { User.delete_all } + + it { should have_selector('div.pagination') } + + it "should list each user" do + User.paginate(page: 1).each do |user| + expect(page).to have_selector('li', text: user.name) + end + end + end + + describe "delete links" do + + it { should_not have_link('delete') } + + describe "as an admin user" do + let(:admin) { FactoryGirl.create(:admin) } + before do + sign_in admin + visit users_path + end + + it { should have_link('delete', href: user_path(User.first)) } + it "should be able to delete another user" do + expect do + click_link('delete', match: :first) + end.to change(User, :count).by(-1) + end + it { should_not have_link('delete', href: user_path(admin)) } + end + end + end + describe "signup page" do before { visit signup_path } it { should have_content('Sign up') } it { should have_title(full_title('Sign up')) } end - + describe "profile page" do let(:user) { FactoryGirl.create(:user) } before { visit user_path(user) } @@ -18,32 +64,89 @@ it { should have_content(user.name) } it { should have_title(user.name) } end - + describe "signup" do - before { visit signup_path } - - let(:submit) { "Create my account" } - - describe "with invalid information" do - it "should not create a user" do - expect { click_button submit }.not_to change(User, :count) - end - end - - describe "with valid information" do - before do - fill_in "Name", with: "Example User" - fill_in "Email", with: "user@example.com" - fill_in "Password", with: "foobar" - fill_in "Confirmation", with: "foobar" - end - - it "should create a user" do - expect { click_button submit }.to change(User, :count).by(1) - end - end - end - -end + before { visit signup_path } + + let(:submit) { "Create my account" } + + describe "with invalid information" do + it "should not create a user" do + expect { click_button submit }.not_to change(User, :count) + end + end + + describe "with valid information" do + before do + fill_in "Name", with: "Example User" + fill_in "Email", with: "user@example.com" + fill_in "Password", with: "foobar" + fill_in "Confirmation", with: "foobar" + end + + it "should create a user" do + expect { click_button submit }.to change(User, :count).by(1) + end + describe "after saving the user" do + before { click_button submit } + let(:user) { User.find_by(email: 'user@example.com') } + + it { should have_link('Sign out') } + it { should have_title(user.name) } + it { should have_selector('div.alert.alert-success', text: 'Welcome') } + end + end + end + + describe "edit" do + let(:user) { FactoryGirl.create(:user) } + before do + sign_in user + visit edit_user_path(user) + end + + describe "page" do + it { should have_content("Update your profile") } + it { should have_title("Edit user") } + it { should have_link('change', href: 'http://gravatar.com/emails') } + end + + describe "with valid information" do + let(:new_name) { "New Name" } + let(:new_email) { "new@example.com" } + before do + fill_in "Name", with: new_name + fill_in "Email", with: new_email + fill_in "Password", with: user.password + fill_in "Confirm Password", with: user.password + click_button "Save changes" + end + + it { should have_title(new_name) } + it { should have_selector('div.alert.alert-success') } + it { should have_link('Sign out', href: signout_path) } + specify { expect(user.reload.name).to eq new_name } + specify { expect(user.reload.email).to eq new_email } + end + end + + describe "profile page" do + let(:user) { FactoryGirl.create(:user) } + let!(:m1) { FactoryGirl.create(:micropost, user: user, content: "Foo") } + let!(:m2) { FactoryGirl.create(:micropost, user: user, content: "Bar") } + + before { visit user_path(user) } + + it { should have_content(user.name) } + it { should have_title(user.name) } + + describe "microposts" do + it { should have_content(m1.content) } + it { should have_content(m2.content) } + it { should have_content(user.microposts.count) } + end + end +end +# diff --git a/kaku/sample_app/spec/support/utilities.rb b/kaku/sample_app/spec/support/utilities.rb index 4764035..0b91857 100644 --- a/kaku/sample_app/spec/support/utilities.rb +++ b/kaku/sample_app/spec/support/utilities.rb @@ -6,3 +6,17 @@ def full_title(page_title) "#{base_title} | #{page_title}" end end + +def sign_in(user, options={}) + if options[:no_capybara] + # Sign in when not using Capybara. + remember_token = User.new_remember_token + cookies[:remember_token] = remember_token + user.update_attribute(:remember_token, User.encrypt(remember_token)) + else + visit signin_path + fill_in "Email", with: user.email + fill_in "Password", with: user.password + click_button "Sign in" + end +end diff --git a/kaku/sample_app/tags b/kaku/sample_app/tags new file mode 100644 index 0000000..edd74b1 --- /dev/null +++ b/kaku/sample_app/tags @@ -0,0 +1,54 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.8 // +AddIndexToUsersEmail db/migrate/20130730142004_add_index_to_users_email.rb /^class AddIndexToUsersEmail < ActiveRecord::Migration$/;" c +AddPasswordDigestToUsers db/migrate/20130730151702_add_password_digest_to_users.rb /^class AddPasswordDigestToUsers < ActiveRecord::Migration$/;" c +AddRememberTokenToUsers db/migrate/20130815143550_add_remember_token_to_users.rb /^class AddRememberTokenToUsers < ActiveRecord::Migration$/;" c +Application config/application.rb /^ class Application < Rails::Application$/;" c class:SampleApp +ApplicationController app/controllers/application_controller.rb /^class ApplicationController < ActionController::Base$/;" c +ApplicationHelper app/helpers/application_helper.rb /^module ApplicationHelper$/;" m +CreateUsers db/migrate/20130730131044_create_users.rb /^class CreateUsers < ActiveRecord::Migration$/;" c +SampleApp config/application.rb /^module SampleApp$/;" m +SessionsController app/controllers/sessions_controller.rb /^class SessionsController < ApplicationController$/;" c +SessionsHelper app/helpers/sessions_helper.rb /^module SessionsHelper$/;" m +StaticPagesController app/controllers/static_pages_controller.rb /^class StaticPagesController < ApplicationController$/;" c +StaticPagesHelper app/helpers/static_pages_helper.rb /^module StaticPagesHelper$/;" m +User app/models/user.rb /^class User < ActiveRecord::Base$/;" c +UsersController app/controllers/users_controller.rb /^class UsersController < ApplicationController$/;" c +UsersHelper app/helpers/users_helper.rb /^module UsersHelper$/;" m +about app/controllers/static_pages_controller.rb /^ def about$/;" f class:StaticPagesController +change db/migrate/20130730131044_create_users.rb /^ def change$/;" f class:CreateUsers +change db/migrate/20130730142004_add_index_to_users_email.rb /^ def change$/;" f class:AddIndexToUsersEmail +change db/migrate/20130730151702_add_password_digest_to_users.rb /^ def change$/;" f class:AddPasswordDigestToUsers +change db/migrate/20130815143550_add_remember_token_to_users.rb /^ def change$/;" f class:AddRememberTokenToUsers +contact app/controllers/static_pages_controller.rb /^ def contact$/;" f class:StaticPagesController +create app/controllers/sessions_controller.rb /^ def create$/;" f class:SessionsController +create app/controllers/users_controller.rb /^ def create$/;" f class:UsersController +create_remember_token app/models/user.rb /^ def create_remember_token$/;" f class:User +currct_user app/controllers/users_controller.rb /^ def currct_user$/;" f class:UsersController +current_user app/helpers/sessions_helper.rb /^ def current_user$/;" f class:SessionsHelper +current_user= app/helpers/sessions_helper.rb /^ def current_user=(user)$/;" f class:SessionsHelper +destroy app/controllers/sessions_controller.rb /^ def destroy$/;" f class:SessionsController +destroy app/controllers/users_controller.rb /^ def destroy$/;" f class:UsersController +edit app/controllers/users_controller.rb /^ def edit$/;" f class:UsersController +encrypt app/models/user.rb /^ def User.encrypt(token)$/;" F class:User +full_title app/helpers/application_helper.rb /^ def full_title(page_title)$/;" f class:ApplicationHelper +full_title spec/support/utilities.rb /^def full_title(page_title)$/;" f +gravatar_for app/helpers/users_helper.rb /^ def gravatar_for(user)$/;" f class:UsersHelper +help app/controllers/static_pages_controller.rb /^ def help$/;" f class:StaticPagesController +home app/controllers/static_pages_controller.rb /^ def home$/;" f class:StaticPagesController +new app/controllers/sessions_controller.rb /^ def new$/;" f class:SessionsController +new app/controllers/users_controller.rb /^ def new$/;" f class:UsersController +new_remember_token app/models/user.rb /^ def User.new_remember_token$/;" F class:User +show app/controllers/users_controller.rb /^ def show$/;" f class:UsersController +sign_in app/helpers/sessions_helper.rb /^ def sign_in(user)$/;" f class:SessionsHelper +sign_in spec/support/utilities.rb /^def sign_in(user, options={})$/;" f +sign_out app/controllers/users_controller.rb /^ def sign_out$/;" f class:UsersController +sign_out app/helpers/sessions_helper.rb /^ def sign_out$/;" f class:SessionsHelper +signed_in? app/helpers/sessions_helper.rb /^ def signed_in?$/;" f class:SessionsHelper +signed_in_user app/controllers/users_controller.rb /^ def signed_in_user$/;" f class:UsersController +update app/controllers/users_controller.rb /^ def update$/;" f class:UsersController +user_params app/controllers/users_controller.rb /^ def user_params$/;" f class:UsersController