From a20678e4d9d4b879154a13d2ac29d9918325f432 Mon Sep 17 00:00:00 2001 From: Mark Goldenson Date: Thu, 9 Apr 2015 16:20:21 -0700 Subject: [PATCH 1/9] Implement #clicks_count to count a link's clicks --- app/controllers/links_controller.rb | 3 ++- app/models/link.rb | 12 ++++++++++++ app/views/links/index.html.erb | 3 ++- .../20150403233303_add_click_count_to_links.rb | 5 +++++ db/schema.rb | 5 +++-- spec/controllers/links_controller_spec.rb | 13 ++++++++++++- spec/helpers/links_helper_spec.rb | 1 - spec/models/link_spec.rb | 12 ++++++++++++ 8 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20150403233303_add_click_count_to_links.rb diff --git a/app/controllers/links_controller.rb b/app/controllers/links_controller.rb index cc2ec1d..4e34d49 100644 --- a/app/controllers/links_controller.rb +++ b/app/controllers/links_controller.rb @@ -10,9 +10,10 @@ def show @link = Link.find_by_short_name(params[:short_name]) if @link + @link.clicked! redirect_to @link.url else - render text: "No such link.", status: 404 + render text: 'No such link.', status: 404 end end diff --git a/app/models/link.rb b/app/models/link.rb index 7972816..affee84 100644 --- a/app/models/link.rb +++ b/app/models/link.rb @@ -3,6 +3,18 @@ class Link < ActiveRecord::Base validates :url, :presence => true + validates :clicks_count, + :numericality => { + :only_integer => true, + :greater_than_or_equal_to => 0 + }, + :presence => true + + def clicked! + self.clicks_count += 1 + self.save + end + # This controls how an ActiveRecord object is displayed in a URL context. # This way, if we do link_path(@link), Rails will generate a path like # "/l/#{@link.short_name}" vs. "/l/#{@link.id}". diff --git a/app/views/links/index.html.erb b/app/views/links/index.html.erb index 92c5393..1dec6bb 100644 --- a/app/views/links/index.html.erb +++ b/app/views/links/index.html.erb @@ -12,7 +12,7 @@ Added Short URL URL - + Clicks @@ -22,6 +22,7 @@ <%= time_ago_in_words(link.created_at) %> ago <%= link_url(link) %> <%= link.url %> + <%= link.clicks_count %> <%= link_to 'Visit link', link %> <% end %> diff --git a/db/migrate/20150403233303_add_click_count_to_links.rb b/db/migrate/20150403233303_add_click_count_to_links.rb new file mode 100644 index 0000000..f1d9fbd --- /dev/null +++ b/db/migrate/20150403233303_add_click_count_to_links.rb @@ -0,0 +1,5 @@ +class AddClickCountToLinks < ActiveRecord::Migration + def change + add_column :links, :clicks_count, :integer, :default => 0, :null => false + end +end diff --git a/db/schema.rb b/db/schema.rb index 184cca6..7c18f04 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,13 +11,14 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20140725003600) do +ActiveRecord::Schema.define(version: 20150403233303) do - create_table "links", force: true do |t| + create_table "links", force: :cascade do |t| t.string "short_name" t.string "url" t.datetime "created_at" t.datetime "updated_at" + t.integer "clicks_count", default: 0, null: false end add_index "links", ["short_name"], name: "index_links_on_short_name", unique: true diff --git a/spec/controllers/links_controller_spec.rb b/spec/controllers/links_controller_spec.rb index ec03d45..8b3e923 100644 --- a/spec/controllers/links_controller_spec.rb +++ b/spec/controllers/links_controller_spec.rb @@ -24,7 +24,8 @@ # Link. As you add validations to Link, be sure to # adjust the attributes here as well. let(:valid_attributes) do - {:url => 'http://example.com/widgets'} + {:url => 'http://example.com/widgets', + :clicks_count => 0} end let(:invalid_attributes) do @@ -53,6 +54,14 @@ get :show, {:short_name => link.to_param}, valid_session expect(response).to redirect_to(link.url) end + + it "increments clicks_count when link is clicked" do + link = Link.create! valid_attributes + get :show, {:short_name => link.to_param}, valid_session + expect { + link.clicked! + }.to change(link, :clicks_count).by(1) + end end describe "GET new" do @@ -95,4 +104,6 @@ end end + describe + end diff --git a/spec/helpers/links_helper_spec.rb b/spec/helpers/links_helper_spec.rb index cb69600..abc9515 100644 --- a/spec/helpers/links_helper_spec.rb +++ b/spec/helpers/links_helper_spec.rb @@ -11,5 +11,4 @@ # end # end RSpec.describe LinksHelper, :type => :helper do - pending "add some examples to (or delete) #{__FILE__}" end diff --git a/spec/models/link_spec.rb b/spec/models/link_spec.rb index c9b9efe..9789c35 100644 --- a/spec/models/link_spec.rb +++ b/spec/models/link_spec.rb @@ -2,6 +2,8 @@ RSpec.describe Link, :type => :model do describe '#valid?' do + it { should validate_numericality_of(:clicks_count).only_integer.is_greater_than_or_equal_to(0) } + it { should validate_presence_of(:clicks_count) } it { should validate_presence_of(:url) } end @@ -15,6 +17,16 @@ end end + describe '#clicked!' do + let(:link) { FactoryGirl.build(:link) } + + it 'increments clicks_count by 1' do + expect { + link.clicked! + }.to change(link, :clicks_count).by(1) + end + end + describe '#to_param' do let(:link) { FactoryGirl.create(:link) } From 9d53c743955d91d3bd33c85784a0e72cafc4f903 Mon Sep 17 00:00:00 2001 From: Mark Goldenson Date: Thu, 9 Apr 2015 16:31:30 -0700 Subject: [PATCH 2/9] Open links in new window --- app/views/links/index.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/links/index.html.erb b/app/views/links/index.html.erb index 1dec6bb..ae2c21e 100644 --- a/app/views/links/index.html.erb +++ b/app/views/links/index.html.erb @@ -23,7 +23,7 @@ <%= link_url(link) %> <%= link.url %> <%= link.clicks_count %> - <%= link_to 'Visit link', link %> + <%= link_to 'Visit link', link, :target => "_blank" %> <% end %> From f1b2b33b0aed0a9a248bf58c326a0a8df6ef9bc2 Mon Sep 17 00:00:00 2001 From: Mark Goldenson Date: Thu, 9 Apr 2015 18:09:52 -0700 Subject: [PATCH 3/9] Validate Link URL --- app/models/link.rb | 48 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/app/models/link.rb b/app/models/link.rb index affee84..8aba388 100644 --- a/app/models/link.rb +++ b/app/models/link.rb @@ -1,5 +1,5 @@ class Link < ActiveRecord::Base - before_create :set_short_name + before_create :set_short_name, :build_url, :validate_url validates :url, :presence => true @@ -24,19 +24,39 @@ def to_param end private - def set_short_name - # Generate and assign a random short_name unless one has already been set. - return self.short_name if self.short_name.present? - - # See: http://www.ruby-doc.org/stdlib-2.1.2/libdoc/securerandom/rdoc/SecureRandom.html#method-c-urlsafe_base64 - # We do this to ensure we're not creating two links with the same short_name - # Since it's randomly generated and not user-supplied, we can't rely on - # validations to do this for us. - try_short_name = SecureRandom.urlsafe_base64(6) - while Link.where(:short_name => try_short_name).any? - try_short_name = SecureRandom.urlsafe_base64(6) + + def validate_url + uri = URI.parse(url) + puts "got here?" + if %w(http https).include?(uri.scheme) + return true + else + uri_error + end + rescue URI::BadURIError + uri_error + rescue URI::InvalidURIError + uri_error end - self.short_name = try_short_name - end + def uri_error + self.errors.add(:url, 'is not a valid URL') + false + end + + def set_short_name + # Generate and assign a random short_name unless one has already been set. + return self.short_name if self.short_name.present? + + # See: http://www.ruby-doc.org/stdlib-2.1.2/libdoc/securerandom/rdoc/SecureRandom.html#method-c-urlsafe_base64 + # We do this to ensure we're not creating two links with the same short_name + # Since it's randomly generated and not user-supplied, we can't rely on + # validations to do this for us. + try_short_name = SecureRandom.urlsafe_base64(6) + while Link.where(:short_name => try_short_name).any? + try_short_name = SecureRandom.urlsafe_base64(6) + end + + self.short_name = try_short_name + end end From dd2c11bcd4a6f87c181cbe7aff3de62fb1084edd Mon Sep 17 00:00:00 2001 From: Mark Goldenson Date: Thu, 9 Apr 2015 18:35:46 -0700 Subject: [PATCH 4/9] Add http prefix to URLs without scheme --- app/models/link.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/models/link.rb b/app/models/link.rb index 8aba388..d07f54a 100644 --- a/app/models/link.rb +++ b/app/models/link.rb @@ -1,5 +1,5 @@ class Link < ActiveRecord::Base - before_create :set_short_name, :build_url, :validate_url + before_create :set_short_name, :validate_url! validates :url, :presence => true @@ -25,20 +25,20 @@ def to_param private - def validate_url + ### IS IT WRONG FOR A VALIDATION TO CHANGE DATA LIKE THIS? + def validate_url! uri = URI.parse(url) - puts "got here?" - if %w(http https).include?(uri.scheme) - return true - else - uri_error - end + add_scheme_to_url_if_none!(uri) rescue URI::BadURIError uri_error rescue URI::InvalidURIError uri_error end + def add_scheme_to_url_if_none!(uri) + url.prepend("http://") unless (uri.kind_of?(URI::HTTP) or uri.kind_of?(URI::HTTPS)) + end + def uri_error self.errors.add(:url, 'is not a valid URL') false From 6951ab642127c0a16b5890ef07ba60c0bd0d8a5a Mon Sep 17 00:00:00 2001 From: Mark Goldenson Date: Mon, 13 Apr 2015 20:23:03 -0700 Subject: [PATCH 5/9] Create Users controller --- app/assets/javascripts/users.js | 2 + app/assets/stylesheets/users.css | 4 ++ app/controllers/links_controller.rb | 8 ++-- app/controllers/users_controller.rb | 37 +++++++++++++++++++ app/helpers/users_helper.rb | 2 + app/models/link.rb | 36 ++++++++++-------- app/models/user.rb | 4 ++ app/views/links/_form.html.erb | 4 +- app/views/links/index.html.erb | 3 ++ app/views/links/new.html.erb | 2 +- app/views/users/_form.html.erb | 29 +++++++++++++++ app/views/users/new.html.erb | 6 +++ config/routes.rb | 10 ++--- db/migrate/20150412214354_create_users.rb | 11 ++++++ .../20150412214410_add_user_to_links.rb | 5 +++ db/schema.rb | 13 ++++++- spec/controllers/links_controller_spec.rb | 4 +- spec/controllers/users_controller_spec.rb | 5 +++ spec/helpers/users_helper_spec.rb | 15 ++++++++ spec/models/user_spec.rb | 5 +++ 20 files changed, 174 insertions(+), 31 deletions(-) create mode 100644 app/assets/javascripts/users.js create mode 100644 app/assets/stylesheets/users.css create mode 100644 app/controllers/users_controller.rb create mode 100644 app/helpers/users_helper.rb create mode 100644 app/models/user.rb create mode 100644 app/views/users/_form.html.erb create mode 100644 app/views/users/new.html.erb create mode 100644 db/migrate/20150412214354_create_users.rb create mode 100644 db/migrate/20150412214410_add_user_to_links.rb create mode 100644 spec/controllers/users_controller_spec.rb create mode 100644 spec/helpers/users_helper_spec.rb create mode 100644 spec/models/user_spec.rb diff --git a/app/assets/javascripts/users.js b/app/assets/javascripts/users.js new file mode 100644 index 0000000..dee720f --- /dev/null +++ b/app/assets/javascripts/users.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/stylesheets/users.css b/app/assets/stylesheets/users.css new file mode 100644 index 0000000..afad32d --- /dev/null +++ b/app/assets/stylesheets/users.css @@ -0,0 +1,4 @@ +/* + Place all the styles related to the matching controller here. + They will automatically be included in application.css. +*/ diff --git a/app/controllers/links_controller.rb b/app/controllers/links_controller.rb index 4e34d49..f1dd779 100644 --- a/app/controllers/links_controller.rb +++ b/app/controllers/links_controller.rb @@ -34,8 +34,8 @@ def create end private - # Only allow a trusted parameter "white list" through. - def link_params - params.require(:link).permit(:url) - end + # Only allow a trusted parameter "white list" through. + def link_params + params.require(:link).permit(:url) + end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..07b390f --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,37 @@ +class UsersController < ApplicationController + def index + end + + def show + end + + def new + @user = User.new + end + + def edit + end + + def create + @user = User.new(user_params) + + if @user.save + # Change to user/:id/show + redirect_to root_url, notice: 'Welcome to url-shortener!' + else + render :new + end + end + + def update + end + + def destroy + end + + private + + def user_params + params.require(:user).permit(:email, :password, :password_confirmation) + end +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..2310a24 --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,2 @@ +module UsersHelper +end diff --git a/app/models/link.rb b/app/models/link.rb index d07f54a..ae8b862 100644 --- a/app/models/link.rb +++ b/app/models/link.rb @@ -1,5 +1,5 @@ class Link < ActiveRecord::Base - before_create :set_short_name, :validate_url! + before_validation :set_short_name, :validate_url_and_prepend_scheme_if_none! validates :url, :presence => true @@ -24,24 +24,28 @@ def to_param end private + # if valid_uri?(uri) + # if uri has no scheme + # uri = prepend http + # if !valid_uri?(uri) + # error + # end + # else + # error + # end - ### IS IT WRONG FOR A VALIDATION TO CHANGE DATA LIKE THIS? - def validate_url! - uri = URI.parse(url) - add_scheme_to_url_if_none!(uri) - rescue URI::BadURIError - uri_error - rescue URI::InvalidURIError - uri_error - end + # def url_must_be_well_formed + # uri = URI.parse(url) + # rescue URI::BadURIError, URI::InvalidURIError + # self.errors.add(:url, 'is not a valid URL') + # end - def add_scheme_to_url_if_none!(uri) - url.prepend("http://") unless (uri.kind_of?(URI::HTTP) or uri.kind_of?(URI::HTTPS)) - end - - def uri_error + # NOT WORKING + def validate_url_and_prepend_scheme_if_none! + uri = URI.parse(url) + url.prepend("http://") unless uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS) + rescue URI::BadURIError, URI::InvalidURIError self.errors.add(:url, 'is not a valid URL') - false end def set_short_name diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..8012cc6 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,4 @@ +class User < ActiveRecord::Base + has_secure_password + validates :email, :presence => true, :uniqueness => true +end diff --git a/app/views/links/_form.html.erb b/app/views/links/_form.html.erb index 4dfa201..e3f076a 100644 --- a/app/views/links/_form.html.erb +++ b/app/views/links/_form.html.erb @@ -1,7 +1,7 @@ -<%= form_for(@link) do |f| %> +<%= form_for @link do |f| %> <% if @link.errors.any? %>
-

<%= pluralize(@link.errors.count, "error") %> prohibited this link from being saved:

+

<%= pluralize(@link.errors.count, "error") %> found:

    <% @link.errors.full_messages.each do |message| %> diff --git a/app/views/links/index.html.erb b/app/views/links/index.html.erb index ae2c21e..c31591d 100644 --- a/app/views/links/index.html.erb +++ b/app/views/links/index.html.erb @@ -6,6 +6,9 @@
<% end %> +

Log In Sign Up +

+ diff --git a/app/views/links/new.html.erb b/app/views/links/new.html.erb index 64c66bf..d188ace 100644 --- a/app/views/links/new.html.erb +++ b/app/views/links/new.html.erb @@ -1,4 +1,4 @@ -

New link

+

New Link

<%= render 'form' %> diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb new file mode 100644 index 0000000..8d4f453 --- /dev/null +++ b/app/views/users/_form.html.erb @@ -0,0 +1,29 @@ +<%= form_for @user do |f| %> + <% if @user.errors.any? %> +
+

<%= pluralize(@user.errors.count, "error") %> found:

+ +
    + <% @user.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+ <% end %> + +
+ <%= f.label :email %>
+ <%= f.text_field :email %> +
+
+ <%= f.label :password %>
+ <%= f.text_field :password %> +
+
+ <%= f.label :password_confirmation %>
+ <%= f.text_field :password_confirmation %> +
+
+ <%= f.submit %> +
+<% end %> diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb new file mode 100644 index 0000000..0049504 --- /dev/null +++ b/app/views/users/new.html.erb @@ -0,0 +1,6 @@ + +

Sign Up

+ +<%= render 'form' %> + +<%= link_to 'Back', links_path %> diff --git a/config/routes.rb b/config/routes.rb index 7b8c84c..c2ef88f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -13,8 +13,7 @@ Rails.application.routes.draw do # Often you'll see this: - # - # resources :links + resources :links, :users # # This is short-hand for the following routes: # @@ -31,9 +30,10 @@ # so these will be our routes. root to: 'links#index' - get '/links/new', to: 'links#new', as: 'new_link' - post '/links', to: 'links#create', as: 'links' - get '/l/:short_name', to: 'links#show', as: 'link' + # get '/links/new', to: 'links#new', as: 'new_link' + # post '/links', to: 'links#create', as: 'links' + # get '/l/:short_name', to: 'links#show', as: 'link' + # get '/users/new', to: 'users#new', as: 'new_user' # "get" tells Rails the HTTP method to look for (GET, in this case) # "/l/:short_name" tells Rails the URL pattern(s) to look for diff --git a/db/migrate/20150412214354_create_users.rb b/db/migrate/20150412214354_create_users.rb new file mode 100644 index 0000000..a20b75e --- /dev/null +++ b/db/migrate/20150412214354_create_users.rb @@ -0,0 +1,11 @@ +class CreateUsers < ActiveRecord::Migration + def change + create_table :users do |t| + t.string :email + t.string :password_digest + + t.timestamps null: false + end + add_index :users, :email, unique: true + end +end diff --git a/db/migrate/20150412214410_add_user_to_links.rb b/db/migrate/20150412214410_add_user_to_links.rb new file mode 100644 index 0000000..fd18a7e --- /dev/null +++ b/db/migrate/20150412214410_add_user_to_links.rb @@ -0,0 +1,5 @@ +class AddUserToLinks < ActiveRecord::Migration + def change + add_reference :links, :user, index: true, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 7c18f04..02affb2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150403233303) do +ActiveRecord::Schema.define(version: 20150412214410) do create_table "links", force: :cascade do |t| t.string "short_name" @@ -19,8 +19,19 @@ t.datetime "created_at" t.datetime "updated_at" t.integer "clicks_count", default: 0, null: false + t.integer "user_id" end add_index "links", ["short_name"], name: "index_links_on_short_name", unique: true + add_index "links", ["user_id"], name: "index_links_on_user_id" + + create_table "users", force: :cascade do |t| + t.string "email" + t.string "password_digest" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "users", ["email"], name: "index_users_on_email", unique: true end diff --git a/spec/controllers/links_controller_spec.rb b/spec/controllers/links_controller_spec.rb index 8b3e923..8e9c765 100644 --- a/spec/controllers/links_controller_spec.rb +++ b/spec/controllers/links_controller_spec.rb @@ -57,9 +57,9 @@ it "increments clicks_count when link is clicked" do link = Link.create! valid_attributes - get :show, {:short_name => link.to_param}, valid_session expect { - link.clicked! + get :show, {:short_name => link.to_param}, valid_session + link.reload }.to change(link, :clicks_count).by(1) end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb new file mode 100644 index 0000000..d3dada6 --- /dev/null +++ b/spec/controllers/users_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe UsersController, :type => :controller do + +end diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb new file mode 100644 index 0000000..0971a2f --- /dev/null +++ b/spec/helpers/users_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the UsersHelper. For example: +# +# describe UsersHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe UsersHelper, :type => :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb new file mode 100644 index 0000000..0bc0e60 --- /dev/null +++ b/spec/models/user_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe User, :type => :model do + pending "add some examples to (or delete) #{__FILE__}" +end From 171d93573bdd79327754e9d1e50745454726631d Mon Sep 17 00:00:00 2001 From: Mark Goldenson Date: Mon, 13 Apr 2015 22:57:53 -0700 Subject: [PATCH 6/9] Implement log in and log out --- app/assets/javascripts/sessions.js | 2 + app/assets/stylesheets/sessions.css | 4 ++ app/controllers/application_controller.rb | 8 ++++ app/controllers/sessions_controller.rb | 40 ++++++++++++++++++++ app/helpers/sessions_helper.rb | 2 + app/views/links/index.html.erb | 8 +++- app/views/sessions/_form.html.erb | 26 +++++++++++++ app/views/sessions/new.html.erb | 3 ++ app/views/users/_form.html.erb | 4 +- config/routes.rb | 6 ++- spec/controllers/sessions_controller_spec.rb | 5 +++ spec/helpers/sessions_helper_spec.rb | 15 ++++++++ 12 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 app/assets/javascripts/sessions.js create mode 100644 app/assets/stylesheets/sessions.css create mode 100644 app/controllers/sessions_controller.rb create mode 100644 app/helpers/sessions_helper.rb create mode 100644 app/views/sessions/_form.html.erb create mode 100644 app/views/sessions/new.html.erb create mode 100644 spec/controllers/sessions_controller_spec.rb create mode 100644 spec/helpers/sessions_helper_spec.rb diff --git a/app/assets/javascripts/sessions.js b/app/assets/javascripts/sessions.js new file mode 100644 index 0000000..dee720f --- /dev/null +++ b/app/assets/javascripts/sessions.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/stylesheets/sessions.css b/app/assets/stylesheets/sessions.css new file mode 100644 index 0000000..afad32d --- /dev/null +++ b/app/assets/stylesheets/sessions.css @@ -0,0 +1,4 @@ +/* + Place all the styles related to the matching controller here. + They will automatically be included in application.css. +*/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d83690e..2d26c5d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,4 +2,12 @@ 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 + + private + + def current_user + @current_user ||= User.find(session[:user_id]) if session[:user_id] + end + + helper_method :current_user end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000..d70a45a --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,40 @@ +class SessionsController < ApplicationController + + def show + end + + def new + @user = User.new + end + + def edit + end + + def create + @user = User.find_by_email(params[:email]) + + if @user && @user.authenticate(params[:password]) + session[:user_id] = @user.id + redirect_to root_url, notice: 'Logged in!' + else + ### Likely not best implementation + @user = User.new + @user.errors.add(:base, 'That email and password were not valid! Please try again.') + render :new + end + end + + def update + end + + def destroy + session[:user_id] = nil + redirect_to root_url, notice: 'Logged out!' + end + + private + + def session_params + # params.require(:session).permit(???) + end +end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb new file mode 100644 index 0000000..309f8b2 --- /dev/null +++ b/app/helpers/sessions_helper.rb @@ -0,0 +1,2 @@ +module SessionsHelper +end diff --git a/app/views/links/index.html.erb b/app/views/links/index.html.erb index c31591d..9759476 100644 --- a/app/views/links/index.html.erb +++ b/app/views/links/index.html.erb @@ -6,7 +6,13 @@ <% end %> -

Log In Sign Up +

<% if current_user %> + Logged in as <%= current_user.email %>. + <%= link_to 'Log Out', logout_path, :method => :delete %> + <% else %> + <%= link_to 'Log In', login_path %> or + <%= link_to 'Sign Up', new_user_path %> + <% end %>

diff --git a/app/views/sessions/_form.html.erb b/app/views/sessions/_form.html.erb new file mode 100644 index 0000000..2b909ef --- /dev/null +++ b/app/views/sessions/_form.html.erb @@ -0,0 +1,26 @@ + +<%= form_tag login_path do %> + <% if @user.errors.any? %> +
+

<%= pluralize(@user.errors.count, "error") %> found:

+ +
    + <% @user.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+ <% end %> + +
+ <%= label_tag :email %>
+ <%= text_field_tag :email, params[:email] %> +
+
+ <%= label_tag :password %>
+ <%= password_field_tag :password %> +
+
+ <%= submit_tag "Log In" %> +
+<% end %> diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb new file mode 100644 index 0000000..7e50ed8 --- /dev/null +++ b/app/views/sessions/new.html.erb @@ -0,0 +1,3 @@ +

Log In

+ +<%= render 'form' %> diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb index 8d4f453..cf7875f 100644 --- a/app/views/users/_form.html.erb +++ b/app/views/users/_form.html.erb @@ -17,11 +17,11 @@
<%= f.label :password %>
- <%= f.text_field :password %> + <%= f.password_field :password %>
<%= f.label :password_confirmation %>
- <%= f.text_field :password_confirmation %> + <%= f.password_field :password_confirmation %>
<%= f.submit %> diff --git a/config/routes.rb b/config/routes.rb index c2ef88f..41a7f43 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,8 +12,12 @@ # !!!IMPORTANT!!! Rails.application.routes.draw do + get '/login', to: 'sessions#new' + post '/login', to: 'sessions#create' + get '/logout', to: 'sessions#destroy' + resources :links, :users + # Often you'll see this: - resources :links, :users # # This is short-hand for the following routes: # diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb new file mode 100644 index 0000000..7c73145 --- /dev/null +++ b/spec/controllers/sessions_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe SessionsController, :type => :controller do + +end diff --git a/spec/helpers/sessions_helper_spec.rb b/spec/helpers/sessions_helper_spec.rb new file mode 100644 index 0000000..48129e2 --- /dev/null +++ b/spec/helpers/sessions_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the SessionsHelper. For example: +# +# describe SessionsHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe SessionsHelper, :type => :helper do + pending "add some examples to (or delete) #{__FILE__}" +end From 74cd3ce89b591ae78ee2bcafad4f293955dda8b3 Mon Sep 17 00:00:00 2001 From: Mark Goldenson Date: Tue, 14 Apr 2015 00:39:02 -0700 Subject: [PATCH 7/9] Broken: trying to implement display of user's links --- app/controllers/application_controller.rb | 6 +++++- app/controllers/links_controller.rb | 9 ++++++++- app/controllers/sessions_controller.rb | 3 +-- app/models/link.rb | 20 +++----------------- app/models/user.rb | 2 ++ 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2d26c5d..e89ee5c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -9,5 +9,9 @@ def current_user @current_user ||= User.find(session[:user_id]) if session[:user_id] end - helper_method :current_user + def user_signed_in? + !current_user.nil? + end + + helper_method :current_user, :user_signed_in? end diff --git a/app/controllers/links_controller.rb b/app/controllers/links_controller.rb index f1dd779..74faa81 100644 --- a/app/controllers/links_controller.rb +++ b/app/controllers/links_controller.rb @@ -1,13 +1,19 @@ class LinksController < ApplicationController # GET /links def index - @links = Link.order('created_at DESC') + if current_user + @links = current_user.links + else + @links = Link.where(user_id: nil).order('created_at DESC') + end end # GET /l/:short_name # See routes.rb for how this is set up. def show + puts params @link = Link.find_by_short_name(params[:short_name]) + puts "Link: #{@link}" if @link @link.clicked! @@ -25,6 +31,7 @@ def new # POST /links def create @link = Link.new(link_params) + @link.user_id = current_user.id if user_signed_in? if @link.save redirect_to root_url, notice: 'Link was successfully created.' diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index d70a45a..b47f176 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,5 +1,4 @@ class SessionsController < ApplicationController - def show end @@ -17,7 +16,7 @@ def create session[:user_id] = @user.id redirect_to root_url, notice: 'Logged in!' else - ### Likely not best implementation + ### IS THERE A BETTER WAY TO SHOW A LOGIN ERROR? @user = User.new @user.errors.add(:base, 'That email and password were not valid! Please try again.') render :new diff --git a/app/models/link.rb b/app/models/link.rb index ae8b862..6c63b0b 100644 --- a/app/models/link.rb +++ b/app/models/link.rb @@ -10,6 +10,8 @@ class Link < ActiveRecord::Base }, :presence => true + belongs_to :user + def clicked! self.clicks_count += 1 self.save @@ -24,23 +26,7 @@ def to_param end private - # if valid_uri?(uri) - # if uri has no scheme - # uri = prepend http - # if !valid_uri?(uri) - # error - # end - # else - # error - # end - - # def url_must_be_well_formed - # uri = URI.parse(url) - # rescue URI::BadURIError, URI::InvalidURIError - # self.errors.add(:url, 'is not a valid URL') - # end - - # NOT WORKING + ### NOT WORKING? def validate_url_and_prepend_scheme_if_none! uri = URI.parse(url) url.prepend("http://") unless uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS) diff --git a/app/models/user.rb b/app/models/user.rb index 8012cc6..d53e4d9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,6 @@ class User < ActiveRecord::Base has_secure_password validates :email, :presence => true, :uniqueness => true + + has_many :links end From ac26b3562f071582b51f3de58163054ba14abd60 Mon Sep 17 00:00:00 2001 From: Mark Goldenson Date: Tue, 14 Apr 2015 00:52:19 -0700 Subject: [PATCH 8/9] Fixed: display user's list of links --- app/models/link.rb | 1 - config/routes.rb | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/models/link.rb b/app/models/link.rb index 6c63b0b..f6919ca 100644 --- a/app/models/link.rb +++ b/app/models/link.rb @@ -26,7 +26,6 @@ def to_param end private - ### NOT WORKING? def validate_url_and_prepend_scheme_if_none! uri = URI.parse(url) url.prepend("http://") unless uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS) diff --git a/config/routes.rb b/config/routes.rb index 41a7f43..60a68b9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,10 +12,16 @@ # !!!IMPORTANT!!! Rails.application.routes.draw do + root to: 'links#index' + get '/links/new', to: 'links#new', as: 'new_link' + post '/links', to: 'links#create', as: 'links' + get '/l/:short_name', to: 'links#show', as: 'link' + get '/login', to: 'sessions#new' post '/login', to: 'sessions#create' get '/logout', to: 'sessions#destroy' - resources :links, :users + + resources :users # Often you'll see this: # @@ -32,13 +38,7 @@ # # For our first application, we can only create and view links, # so these will be our routes. - - root to: 'links#index' - # get '/links/new', to: 'links#new', as: 'new_link' - # post '/links', to: 'links#create', as: 'links' - # get '/l/:short_name', to: 'links#show', as: 'link' - # get '/users/new', to: 'users#new', as: 'new_user' - + # # "get" tells Rails the HTTP method to look for (GET, in this case) # "/l/:short_name" tells Rails the URL pattern(s) to look for # "to: 'links#show'" tells Rails to call the show method on links_controller From 2cd11c6211599b5dccd48dfaf188bd182538bb44 Mon Sep 17 00:00:00 2001 From: Mark Goldenson Date: Tue, 14 Apr 2015 12:18:28 -0700 Subject: [PATCH 9/9] Delete link if authorized or logged out --- app/controllers/application_controller.rb | 20 +++++++++++++++++++- app/controllers/links_controller.rb | 13 ++++++++++--- app/controllers/sessions_controller.rb | 3 +-- app/controllers/users_controller.rb | 3 +-- app/views/layouts/application.html.erb | 1 + app/views/links/index.html.erb | 7 +++++-- config/routes.rb | 13 +++++++------ 7 files changed, 44 insertions(+), 16 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e89ee5c..b3c6a3d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -13,5 +13,23 @@ def user_signed_in? !current_user.nil? end - helper_method :current_user, :user_signed_in? + def user_signed_out? + current_user.nil? + end + + def sign_in!(user) + session[:user_id] = user.id + @current_user = user + end + + def sign_out! + @current_user = nil + session.delete(:user_id) + end + + def authorize + redirect_to login_url, alert: 'Please log in to access that.' if user_signed_out? + end + + helper_method :current_user, :user_signed_in?, :user_signed_out?, :sign_in!, :sign_out!, :authorize end diff --git a/app/controllers/links_controller.rb b/app/controllers/links_controller.rb index 74faa81..81e74d5 100644 --- a/app/controllers/links_controller.rb +++ b/app/controllers/links_controller.rb @@ -1,4 +1,6 @@ class LinksController < ApplicationController + before_filter :authorize, only: [:destroy] + # GET /links def index if current_user @@ -11,9 +13,7 @@ def index # GET /l/:short_name # See routes.rb for how this is set up. def show - puts params @link = Link.find_by_short_name(params[:short_name]) - puts "Link: #{@link}" if @link @link.clicked! @@ -40,9 +40,16 @@ def create end end + def destroy + @link = Link.find_by_short_name(params[:short_name]) + @link.destroy + + redirect_to action: :index, notice: 'Link deleted!' + end + private # Only allow a trusted parameter "white list" through. def link_params - params.require(:link).permit(:url) + params.require(:link).permit(:url, :shortname) end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index b47f176..27ddd2e 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -13,7 +13,7 @@ def create @user = User.find_by_email(params[:email]) if @user && @user.authenticate(params[:password]) - session[:user_id] = @user.id + sign_in!(@user) redirect_to root_url, notice: 'Logged in!' else ### IS THERE A BETTER WAY TO SHOW A LOGIN ERROR? @@ -32,7 +32,6 @@ def destroy end private - def session_params # params.require(:session).permit(???) end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 07b390f..f435a7d 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -16,7 +16,7 @@ def create @user = User.new(user_params) if @user.save - # Change to user/:id/show + sign_in!(@user) redirect_to root_url, notice: 'Welcome to url-shortener!' else render :new @@ -30,7 +30,6 @@ def destroy end private - def user_params params.require(:user).permit(:email, :password, :password_confirmation) end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index dd67306..69ec005 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -4,6 +4,7 @@ UrlShortener <%= stylesheet_link_tag 'application', media: 'all' %> <%= csrf_meta_tags %> + <%= javascript_include_tag "application" %> diff --git a/app/views/links/index.html.erb b/app/views/links/index.html.erb index 9759476..2ac446f 100644 --- a/app/views/links/index.html.erb +++ b/app/views/links/index.html.erb @@ -6,7 +6,7 @@
<% end %> -

<% if current_user %> +

<% if user_signed_in? %> Logged in as <%= current_user.email %>. <%= link_to 'Log Out', logout_path, :method => :delete %> <% else %> @@ -32,7 +32,10 @@

- + + <% if user_signed_out? || (user_signed_in? && link.user == current_user) %> + + <% end %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index 60a68b9..de619d6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -13,13 +13,14 @@ Rails.application.routes.draw do root to: 'links#index' - get '/links/new', to: 'links#new', as: 'new_link' - post '/links', to: 'links#create', as: 'links' - get '/l/:short_name', to: 'links#show', as: 'link' + get '/links/new', to: 'links#new', as: 'new_link' + post '/links', to: 'links#create', as: 'links' + get '/l/:short_name', to: 'links#show', as: 'link' + delete '/l/:short_name', to: 'links#destroy' - get '/login', to: 'sessions#new' - post '/login', to: 'sessions#create' - get '/logout', to: 'sessions#destroy' + get '/login', to: 'sessions#new' + post '/login', to: 'sessions#create' + delete '/logout', to: 'sessions#destroy' resources :users
<%= link_url(link) %> <%= link.url %> <%= link.clicks_count %><%= link_to 'Visit link', link, :target => "_blank" %><%= link_to 'Visit Link', link, :target => '_blank' %><%= link_to 'Delete Link', link, :method => :delete %>