diff --git a/source/Gemfile b/source/Gemfile index 9627b8b..c915f50 100644 --- a/source/Gemfile +++ b/source/Gemfile @@ -27,7 +27,7 @@ gem 'sdoc', '~> 0.4.0', group: :doc gem 'spring', group: :development # Use ActiveModel has_secure_password -# gem 'bcrypt', '~> 3.1.7' +gem 'bcrypt', '~> 3.1.7' # Use unicorn as the app server # gem 'unicorn' @@ -39,3 +39,5 @@ gem 'spring', group: :development # gem 'debugger', group: [:development, :test] gem 'rspec-rails', group: [:development, :test] + gem 'pry' + gem 'faraday' diff --git a/source/Gemfile.lock b/source/Gemfile.lock index fcf8b98..1f37a5d 100644 --- a/source/Gemfile.lock +++ b/source/Gemfile.lock @@ -28,33 +28,43 @@ GEM thread_safe (~> 0.1) tzinfo (~> 1.1) arel (5.0.1.20140414130214) + bcrypt (3.1.10) builder (3.2.2) + coderay (1.1.0) coffee-rails (4.0.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) - coffee-script (2.3.0) + coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.8.0) + coffee-script-source (1.9.1.1) diff-lcs (1.2.5) erubis (2.7.0) - execjs (2.2.1) + execjs (2.5.2) + faraday (0.9.1) + multipart-post (>= 1.2, < 3) hike (1.2.3) - i18n (0.6.11) - jbuilder (2.2.2) + i18n (0.7.0) + jbuilder (2.2.16) activesupport (>= 3.0.0, < 5) multi_json (~> 1.2) jquery-rails (3.1.2) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - json (1.8.1) - mail (2.6.1) + json (1.8.3) + mail (2.6.3) mime-types (>= 1.16, < 3) - mime-types (2.4.1) - minitest (5.4.2) - multi_json (1.10.1) - rack (1.5.2) - rack-test (0.6.2) + method_source (0.8.2) + mime-types (2.6.1) + minitest (5.7.0) + multi_json (1.11.0) + multipart-post (2.0.0) + pry (0.10.1) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + rack (1.5.3) + rack-test (0.6.3) rack (>= 1.0) rails (4.1.6) actionmailer (= 4.1.6) @@ -71,53 +81,55 @@ GEM activesupport (= 4.1.6) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (10.3.2) - rdoc (4.1.2) + rake (10.4.2) + rdoc (4.2.0) json (~> 1.4) - rspec-core (3.1.6) - rspec-support (~> 3.1.0) - rspec-expectations (3.1.2) + rspec-core (3.2.3) + rspec-support (~> 3.2.0) + rspec-expectations (3.2.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.1.0) - rspec-mocks (3.1.3) - rspec-support (~> 3.1.0) - rspec-rails (3.1.0) - actionpack (>= 3.0) - activesupport (>= 3.0) - railties (>= 3.0) - rspec-core (~> 3.1.0) - rspec-expectations (~> 3.1.0) - rspec-mocks (~> 3.1.0) - rspec-support (~> 3.1.0) - rspec-support (3.1.2) + rspec-support (~> 3.2.0) + rspec-mocks (3.2.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.2.0) + rspec-rails (3.2.3) + actionpack (>= 3.0, < 4.3) + activesupport (>= 3.0, < 4.3) + railties (>= 3.0, < 4.3) + rspec-core (~> 3.2.0) + rspec-expectations (~> 3.2.0) + rspec-mocks (~> 3.2.0) + rspec-support (~> 3.2.0) + rspec-support (3.2.2) sass (3.2.19) - sass-rails (4.0.3) + sass-rails (4.0.5) railties (>= 4.0.0, < 5.0) - sass (~> 3.2.0) - sprockets (~> 2.8, <= 2.11.0) + sass (~> 3.2.2) + sprockets (~> 2.8, < 3.0) sprockets-rails (~> 2.0) sdoc (0.4.1) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) - spring (1.1.3) - sprockets (2.11.0) + slop (3.6.0) + spring (1.3.6) + sprockets (2.12.3) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.2.0) + sprockets-rails (2.3.1) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) - sqlite3 (1.3.9) + sqlite3 (1.3.10) thor (0.19.1) - thread_safe (0.3.4) + thread_safe (0.3.5) tilt (1.4.1) - turbolinks (2.4.0) + turbolinks (2.5.3) coffee-rails tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (2.5.3) + uglifier (2.7.1) execjs (>= 0.3.0) json (>= 1.8.0) @@ -125,9 +137,12 @@ PLATFORMS ruby DEPENDENCIES + bcrypt (~> 3.1.7) coffee-rails (~> 4.0.0) + faraday jbuilder (~> 2.0) jquery-rails + pry rails (= 4.1.6) rspec-rails sass-rails (~> 4.0.3) @@ -136,3 +151,6 @@ DEPENDENCIES sqlite3 turbolinks uglifier (>= 1.3.0) + +BUNDLED WITH + 1.10.3 diff --git a/source/app/assets/stylesheets/application.css b/source/app/assets/stylesheets/application.css index a443db3..9470714 100644 --- a/source/app/assets/stylesheets/application.css +++ b/source/app/assets/stylesheets/application.css @@ -13,3 +13,9 @@ *= require_tree . *= require_self */ + .field_with_errors { + @extend .has-error; + .form-control { + color: $state-danger-text; + } + } diff --git a/source/app/assets/stylesheets/scaffolds.css.scss b/source/app/assets/stylesheets/scaffolds.css.scss new file mode 100644 index 0000000..6ec6a8f --- /dev/null +++ b/source/app/assets/stylesheets/scaffolds.css.scss @@ -0,0 +1,69 @@ +body { + background-color: #fff; + color: #333; + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { + color: #000; + &:visited { + color: #666; + } + &:hover { + color: #fff; + background-color: #000; + } +} + +div { + &.field, &.actions { + margin-bottom: 10px; + } +} + +#notice { + color: green; +} + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px; + padding-bottom: 0; + margin-bottom: 20px; + background-color: #f0f0f0; + h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + margin-bottom: 0px; + background-color: #c00; + color: #fff; + } + ul li { + font-size: 12px; + list-style: square; + } +} diff --git a/source/app/assets/stylesheets/urls.css.scss b/source/app/assets/stylesheets/urls.css.scss index a4281ec..87f0d17 100644 --- a/source/app/assets/stylesheets/urls.css.scss +++ b/source/app/assets/stylesheets/urls.css.scss @@ -1,3 +1,9 @@ // Place all the styles related to the Urls controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ +table { + width: 100%; + td { + border: 1px solid black; + } +} diff --git a/source/app/controllers/application_controller.rb b/source/app/controllers/application_controller.rb index d83690e..d8b3d09 100644 --- a/source/app/controllers/application_controller.rb +++ b/source/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/source/app/controllers/sessions_controller.rb b/source/app/controllers/sessions_controller.rb index 16d11b5..e00b760 100644 --- a/source/app/controllers/sessions_controller.rb +++ b/source/app/controllers/sessions_controller.rb @@ -1,2 +1,23 @@ class SessionsController < ApplicationController + + def new + end + + def create + user = User.find_by(email: params[:session][:email].downcase) + if user && user.authenticate(params[:session][:password]) + log_in user + flash[:success] = "You are now logged in" + redirect_to urls_path + else + flash.now[:danger] = "User could not be authenticated" + render 'new' + end + end + + def destroy + log_out + flash[:success] = "You are logged out" + redirect_to root_url + end end diff --git a/source/app/controllers/urls_controller.rb b/source/app/controllers/urls_controller.rb index ef26710..0b2c104 100644 --- a/source/app/controllers/urls_controller.rb +++ b/source/app/controllers/urls_controller.rb @@ -1,2 +1,84 @@ +require 'pry' + class UrlsController < ApplicationController + + before_action :get_url, only: [:show, :destroy] + before_action :logged_in_user, only: [:edit, :update, :create, :destroy] + before_action :correct_user, only: [:edit, :update, :destroy] + + def index + @urls = Url.all + end + + def create + @url = Url.new(url_params) + @url.user_id = current_user + if @url.save + flash[:success] = 'Url was successfully created' + redirect_to urls_path + else + flash[:failure] = 'Url could not be created' + render 'new' + end + end + + def new + @url = Url.new + end + + def edit + @url = get_url + end + + def update + @url = get_url + if @url.update(url_params) + flash[:success] = "Url successfully updated" + redirect_to urls_path + else + render 'edit' + end + end + + def destroy + @url.destroy + redirect_to_index + end + + def expand_link + @url = Url.find_by unique_key: params[:unique_key] + redirect_to_url + end + + def redirect_to_index + redirect_to urls_path + end + + def redirect_to_url + @url.click_count += 1 + @url.save + redirect_to @url.address + end + + def get_url + @url = Url.find(params[:id]) + end + + private + + def url_params + params.require(:url).permit(:address) + end + + def logged_in_user + unless logged_in? + flash[:danger] = "Please log in" + redirect_to login_url + end + end + + def correct_user + user = User.find(get_url.user_id) + redirect_to(root_url) unless current_user?(user) || admin? + end end diff --git a/source/app/controllers/users_controller.rb b/source/app/controllers/users_controller.rb index 3e74dea..ca6780b 100644 --- a/source/app/controllers/users_controller.rb +++ b/source/app/controllers/users_controller.rb @@ -1,2 +1,23 @@ class UsersController < ApplicationController + + def new + @user = User.new + end + + def create + @user = User.new(user_params) + if @user.save + log_in @user + flash[:success] = "User successfully created" + redirect_to urls_path + else + render 'new' + end + end + + private + + def user_params + params.require(:user).permit(:name, :email, :password, :password_confirmation) + end end diff --git a/source/app/helpers/sessions_helper.rb b/source/app/helpers/sessions_helper.rb index 309f8b2..e0f78ca 100644 --- a/source/app/helpers/sessions_helper.rb +++ b/source/app/helpers/sessions_helper.rb @@ -1,2 +1,26 @@ module SessionsHelper + def log_in(user) + session[:user_id] = user.id + end + + def current_user + @current_user ||= User.find_by(id: session[:user_id]) + end + + def logged_in? + !current_user.nil? + end + + def log_out + session.delete(:user_id) + @current_user = nil + end + + def current_user?(user) + user == current_user + end + + def admin? + current_user.admin + end end diff --git a/source/app/models/url.rb b/source/app/models/url.rb new file mode 100644 index 0000000..23929c5 --- /dev/null +++ b/source/app/models/url.rb @@ -0,0 +1,42 @@ +require 'uri' +require "net/http" +require 'faraday' + +class Url < ActiveRecord::Base + before_save :shorten_address + validate :validate_url + # validates :address, :format => URI::regexp(%w(http https)) + validates_uniqueness_of :unique_key + + private + + def validate_url + if self.address.match(/^https?:\/\//).nil? + errors.add(:address, "needs to be a valid url") + else + if self.address.match(URI::regexp(%w(http https))).nil? + errors.add(:address, "needs to be a valid url") + end + + begin + response = send_http_request(self.address) + if response.status.to_i >= 400 + errors.add(:address, "needs to be a valid url") + end + rescue Faraday::ConnectionFailed + errors.add(:address, "needs to be a valid url") + end + end + end + + def shorten_address + if self.unique_key.nil? + self.unique_key = 8.times.map { [*'0'..'9', *'a'..'z'].sample }.join + end + end + + def send_http_request url + Faraday.head url + end + +end diff --git a/source/app/models/user.rb b/source/app/models/user.rb new file mode 100644 index 0000000..d52449d --- /dev/null +++ b/source/app/models/user.rb @@ -0,0 +1,10 @@ +class User < ActiveRecord::Base + before_save { self.email = email.downcase } + VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i + validates :email, presence: true, + length: { maximum: 255 }, + format: { with: VALID_EMAIL_REGEX }, + uniqueness: { case_sensitive: false } + has_secure_password + validates :password, presence: true, length: { minimum: 6 } +end diff --git a/source/app/views/layouts/application.html.erb b/source/app/views/layouts/application.html.erb index f946432..153ce37 100644 --- a/source/app/views/layouts/application.html.erb +++ b/source/app/views/layouts/application.html.erb @@ -7,8 +7,24 @@ <%= csrf_meta_tags %> +
+ <% if logged_in? %> +

<%= link_to "Log out", logout_path, method: :delete %>

+ <% if admin? %> +

YOU'RE AN ADMIN!

+ <% end %> + <% else %> +

<%= link_to "Log in", login_path %>

+ <% end %> +
+ + <% flash.each do |message_type, message| %> +
<%= message %>
+ <% end %> <%= yield %> +<%= debug(params) if Rails.env.development? %> + diff --git a/source/app/views/sessions/new.html.erb b/source/app/views/sessions/new.html.erb new file mode 100644 index 0000000..8bc7a43 --- /dev/null +++ b/source/app/views/sessions/new.html.erb @@ -0,0 +1,18 @@ +

Log in

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

<%= link_to "New User?", signup_path %>

+
+
diff --git a/source/app/views/shared/_error_messages.html.erb b/source/app/views/shared/_error_messages.html.erb new file mode 100644 index 0000000..f80053e --- /dev/null +++ b/source/app/views/shared/_error_messages.html.erb @@ -0,0 +1,12 @@ +<% if @user.errors.any? %> +
+
+ The form contains <%= pluralize(@user.errors.count, "error") %>. +
+ +
+<% end %> diff --git a/source/app/views/urls/_form.html.erb b/source/app/views/urls/_form.html.erb new file mode 100644 index 0000000..c423725 --- /dev/null +++ b/source/app/views/urls/_form.html.erb @@ -0,0 +1,20 @@ +<%= form_for @url, url: {action: "create"} do |f| %> + + <% if @url.errors.any? %> +
+ +
+ <% end %> + +
+

Url: <%= f.text_field :address %>

+

+ +
+ <%= f.submit %> +
+<% end %> diff --git a/source/app/views/urls/edit.html.erb b/source/app/views/urls/edit.html.erb new file mode 100644 index 0000000..a185b31 --- /dev/null +++ b/source/app/views/urls/edit.html.erb @@ -0,0 +1,14 @@ +

Update link

+ +
+
+ <%= form_for(@url) do |f| %> + + <%= f.label :address %> + <%= f.text_field :address, class: 'form-control' %> + + <%= f.submit "Save changes", class: "btn btn-primary" %> + <% end %> + +
+
diff --git a/source/app/views/urls/index.html.erb b/source/app/views/urls/index.html.erb new file mode 100644 index 0000000..b243420 --- /dev/null +++ b/source/app/views/urls/index.html.erb @@ -0,0 +1,28 @@ +

Listing Urls

+ + + + + + + + + + <% @urls.each do |url| %> + + + + + + + + <% end %> + <% if @urls.empty? %> +

There are no urls

+ <% end %> + +
<%= link_to root_url + url.unique_key, url.unique_key %><%= url.address %><%= url.click_count %><%= link_to 'Edit', edit_url_path(url) %><%= link_to 'Destroy', url, method: :delete, data: { confirm: 'Are you sure?' } %>
+ +
+ +<%= link_to 'New Url', new_url_path %> diff --git a/source/app/views/urls/index.json.jbuilder b/source/app/views/urls/index.json.jbuilder new file mode 100644 index 0000000..8291267 --- /dev/null +++ b/source/app/views/urls/index.json.jbuilder @@ -0,0 +1,4 @@ +json.array!(@urls) do |url| + json.extract! url, :id + json.url url_url(url, format: :json) +end diff --git a/source/app/views/urls/new.html.erb b/source/app/views/urls/new.html.erb new file mode 100644 index 0000000..7e711d5 --- /dev/null +++ b/source/app/views/urls/new.html.erb @@ -0,0 +1,5 @@ +

New url

+ +<%= render 'form' %> + +<%= link_to 'Back', urls_path %> diff --git a/source/app/views/urls/show.html.erb b/source/app/views/urls/show.html.erb new file mode 100644 index 0000000..2e24321 --- /dev/null +++ b/source/app/views/urls/show.html.erb @@ -0,0 +1,5 @@ +

<%= notice %>

+ +

<%= root_url + @url.unique_key %>

+ +<%= link_to 'Back', urls_path %> diff --git a/source/app/views/urls/show.json.jbuilder b/source/app/views/urls/show.json.jbuilder new file mode 100644 index 0000000..7e2f9a0 --- /dev/null +++ b/source/app/views/urls/show.json.jbuilder @@ -0,0 +1 @@ +json.extract! @url, :id, :created_at, :updated_at diff --git a/source/app/views/users/new.html.erb b/source/app/views/users/new.html.erb new file mode 100644 index 0000000..b072893 --- /dev/null +++ b/source/app/views/users/new.html.erb @@ -0,0 +1,20 @@ +

Sign up

+ +
+
+ <%= form_for(@user) do |f| %> + <%= render 'shared/error_messages' %> + + <%= f.label :email %> + <%= f.text_field :email, class: 'form_control' %> + + <%= f.label :password %> + <%= f.password_field :password, class: 'form_control' %> + + <%= f.label :password_confirmation, "Confirmation" %> + <%= f.password_field :password_confirmation, class: 'form_control' %> + + <%= f.submit "Create my account", class: "btn btn-primary" %> + <% end %> +
+
diff --git a/source/config/routes.rb b/source/config/routes.rb index 3f66539..b7217fd 100644 --- a/source/config/routes.rb +++ b/source/config/routes.rb @@ -1,4 +1,21 @@ Rails.application.routes.draw do + get 'sessions/new' + + get 'users/new' + + root 'urls#index' + + get '/urls', to: 'urls#index' + get '/signup', to: 'users#new' + get '/login', to: 'sessions#new' + post '/login', to: 'sessions#create' + delete '/logout', to: "sessions#destroy" + + get '/:unique_key', to: 'urls#expand_link' + + resources :urls, except: [:index] + resources :users, except: [:show, :edit, :update, :index, :destroy] + # 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/source/db/migrate/20150610161621_url.rb b/source/db/migrate/20150610161621_url.rb new file mode 100644 index 0000000..a32632c --- /dev/null +++ b/source/db/migrate/20150610161621_url.rb @@ -0,0 +1,4 @@ +class Url < ActiveRecord::Migration + def change + end +end diff --git a/source/db/migrate/20150610172307_create_urls.rb b/source/db/migrate/20150610172307_create_urls.rb new file mode 100644 index 0000000..f03f568 --- /dev/null +++ b/source/db/migrate/20150610172307_create_urls.rb @@ -0,0 +1,10 @@ +class CreateUrls < ActiveRecord::Migration + def change + create_table :urls do |t| + t.string :address + t.string :unique_key + + t.timestamps + end + end +end diff --git a/source/db/migrate/20150610180341_add_count.rb b/source/db/migrate/20150610180341_add_count.rb new file mode 100644 index 0000000..2afe41c --- /dev/null +++ b/source/db/migrate/20150610180341_add_count.rb @@ -0,0 +1,7 @@ +class AddCount < ActiveRecord::Migration + def change + change_table :urls do |t| + t.integer :click_count, :default => 0 + end + end +end diff --git a/source/db/migrate/20150611014731_create_users.rb b/source/db/migrate/20150611014731_create_users.rb new file mode 100644 index 0000000..b5dc108 --- /dev/null +++ b/source/db/migrate/20150611014731_create_users.rb @@ -0,0 +1,9 @@ +class CreateUsers < ActiveRecord::Migration + def change + create_table :users do |t| + t.string :email + + t.timestamps + end + end +end diff --git a/source/db/migrate/20150611132414_add_index_to_users_email.rb b/source/db/migrate/20150611132414_add_index_to_users_email.rb new file mode 100644 index 0000000..ca59a67 --- /dev/null +++ b/source/db/migrate/20150611132414_add_index_to_users_email.rb @@ -0,0 +1,5 @@ +class AddIndexToUsersEmail < ActiveRecord::Migration + def change + add_index :users, :email, unique: true + end +end diff --git a/source/db/migrate/20150611133240_add_password_digest_to_users.rb b/source/db/migrate/20150611133240_add_password_digest_to_users.rb new file mode 100644 index 0000000..7ad1f62 --- /dev/null +++ b/source/db/migrate/20150611133240_add_password_digest_to_users.rb @@ -0,0 +1,5 @@ +class AddPasswordDigestToUsers < ActiveRecord::Migration + def change + add_column :users, :password_digest, :string + end +end diff --git a/source/db/migrate/20150611153619_add_foreign_key_to_urls.rb b/source/db/migrate/20150611153619_add_foreign_key_to_urls.rb new file mode 100644 index 0000000..fc47274 --- /dev/null +++ b/source/db/migrate/20150611153619_add_foreign_key_to_urls.rb @@ -0,0 +1,5 @@ +class AddForeignKeyToUrls < ActiveRecord::Migration + def change + add_column :urls, :user_id, :foreign_key + end +end diff --git a/source/db/migrate/20150611173030_addadmin.rb b/source/db/migrate/20150611173030_addadmin.rb new file mode 100644 index 0000000..db45695 --- /dev/null +++ b/source/db/migrate/20150611173030_addadmin.rb @@ -0,0 +1,5 @@ +class Addadmin < ActiveRecord::Migration + def change + add_column :users, :admin, :boolean, default: false + end +end diff --git a/source/db/schema.rb b/source/db/schema.rb new file mode 100644 index 0000000..58b0bfc --- /dev/null +++ b/source/db/schema.rb @@ -0,0 +1,29 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20150611173030) do + +# Could not dump table "urls" because of following NoMethodError +# undefined method `[]' for nil:NilClass + + create_table "users", force: true do |t| + t.string "email" + t.datetime "created_at" + t.datetime "updated_at" + t.string "password_digest" + t.boolean "admin", default: false + end + + add_index "users", ["email"], name: "index_users_on_email", unique: true + +end diff --git a/source/spec/controllers/sessions_controller_spec.rb b/source/spec/controllers/sessions_controller_spec.rb new file mode 100644 index 0000000..f524aa5 --- /dev/null +++ b/source/spec/controllers/sessions_controller_spec.rb @@ -0,0 +1,12 @@ +require 'rails_helper' + +RSpec.describe SessionsController, type: :controller do + + describe "GET #new" do + it "returns http success" do + get :new + expect(response).to have_http_status(:success) + end + end + +end diff --git a/source/spec/controllers/urls_controller_spec.rb b/source/spec/controllers/urls_controller_spec.rb new file mode 100644 index 0000000..7014ddd --- /dev/null +++ b/source/spec/controllers/urls_controller_spec.rb @@ -0,0 +1,159 @@ +require 'rails_helper' + +# This spec was generated by rspec-rails when you ran the scaffold generator. +# It demonstrates how one might use RSpec to specify the controller code that +# was generated by Rails when you ran the scaffold generator. +# +# It assumes that the implementation code is generated by the rails scaffold +# generator. If you are using any extension libraries to generate different +# controller code, this generated spec may or may not pass. +# +# It only uses APIs available in rails and/or rspec-rails. There are a number +# of tools you can use to make these specs even more expressive, but we're +# sticking to rails and rspec-rails APIs to keep things simple and stable. +# +# Compared to earlier versions of this generator, there is very limited use of +# stubs and message expectations in this spec. Stubs are only used when there +# is no simpler way to get a handle on the object needed for the example. +# Message expectations are only used when there is no simpler way to specify +# that an instance is receiving a specific message. + +RSpec.describe UrlsController, :type => :controller do + + # This should return the minimal set of attributes required to create a valid + # Url. As you add validations to Url, be sure to + # adjust the attributes here as well. + let(:valid_attributes) { + skip("Add a hash of attributes valid for your model") + } + + let(:invalid_attributes) { + skip("Add a hash of attributes invalid for your model") + } + + # This should return the minimal set of values that should be in the session + # in order to pass any filters (e.g. authentication) defined in + # UrlsController. Be sure to keep this updated too. + let(:valid_session) { {} } + + describe "GET index" do + it "assigns all urls as @urls" do + url = Url.create! valid_attributes + get :index, {}, valid_session + expect(assigns(:urls)).to eq([url]) + end + end + + describe "GET show" do + it "assigns the requested url as @url" do + url = Url.create! valid_attributes + get :show, {:id => url.to_param}, valid_session + expect(assigns(:url)).to eq(url) + end + end + + describe "GET new" do + it "assigns a new url as @url" do + get :new, {}, valid_session + expect(assigns(:url)).to be_a_new(Url) + end + end + + describe "GET edit" do + it "assigns the requested url as @url" do + url = Url.create! valid_attributes + get :edit, {:id => url.to_param}, valid_session + expect(assigns(:url)).to eq(url) + end + end + + describe "POST create" do + describe "with valid params" do + it "creates a new Url" do + expect { + post :create, {:url => valid_attributes}, valid_session + }.to change(Url, :count).by(1) + end + + it "assigns a newly created url as @url" do + post :create, {:url => valid_attributes}, valid_session + expect(assigns(:url)).to be_a(Url) + expect(assigns(:url)).to be_persisted + end + + it "redirects to the created url" do + post :create, {:url => valid_attributes}, valid_session + expect(response).to redirect_to(Url.last) + end + end + + describe "with invalid params" do + it "assigns a newly created but unsaved url as @url" do + post :create, {:url => invalid_attributes}, valid_session + expect(assigns(:url)).to be_a_new(Url) + end + + it "re-renders the 'new' template" do + post :create, {:url => invalid_attributes}, valid_session + expect(response).to render_template("new") + end + end + end + + describe "PUT update" do + describe "with valid params" do + let(:new_attributes) { + skip("Add a hash of attributes valid for your model") + } + + it "updates the requested url" do + url = Url.create! valid_attributes + put :update, {:id => url.to_param, :url => new_attributes}, valid_session + url.reload + skip("Add assertions for updated state") + end + + it "assigns the requested url as @url" do + url = Url.create! valid_attributes + put :update, {:id => url.to_param, :url => valid_attributes}, valid_session + expect(assigns(:url)).to eq(url) + end + + it "redirects to the url" do + url = Url.create! valid_attributes + put :update, {:id => url.to_param, :url => valid_attributes}, valid_session + expect(response).to redirect_to(url) + end + end + + describe "with invalid params" do + it "assigns the url as @url" do + url = Url.create! valid_attributes + put :update, {:id => url.to_param, :url => invalid_attributes}, valid_session + expect(assigns(:url)).to eq(url) + end + + it "re-renders the 'edit' template" do + url = Url.create! valid_attributes + put :update, {:id => url.to_param, :url => invalid_attributes}, valid_session + expect(response).to render_template("edit") + end + end + end + + describe "DELETE destroy" do + it "destroys the requested url" do + url = Url.create! valid_attributes + expect { + delete :destroy, {:id => url.to_param}, valid_session + }.to change(Url, :count).by(-1) + end + + it "redirects to the urls list" do + url = Url.create! valid_attributes + delete :destroy, {:id => url.to_param}, valid_session + expect(response).to redirect_to(urls_url) + end + end + +end diff --git a/source/spec/controllers/users_controller_spec.rb b/source/spec/controllers/users_controller_spec.rb new file mode 100644 index 0000000..0a86999 --- /dev/null +++ b/source/spec/controllers/users_controller_spec.rb @@ -0,0 +1,12 @@ +require 'rails_helper' + +RSpec.describe UsersController, type: :controller do + + describe "GET #new" do + it "returns http success" do + get :new + expect(response).to have_http_status(:success) + end + end + +end diff --git a/source/spec/helpers/sessions_helper_spec.rb b/source/spec/helpers/sessions_helper_spec.rb new file mode 100644 index 0000000..9484198 --- /dev/null +++ b/source/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 diff --git a/source/spec/helpers/urls_helper_spec.rb b/source/spec/helpers/urls_helper_spec.rb new file mode 100644 index 0000000..bbafaf3 --- /dev/null +++ b/source/spec/helpers/urls_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the UrlsHelper. For example: +# +# describe UrlsHelper 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 UrlsHelper, :type => :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/source/spec/helpers/users_helper_spec.rb b/source/spec/helpers/users_helper_spec.rb new file mode 100644 index 0000000..b2e3444 --- /dev/null +++ b/source/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/source/spec/models/url_spec.rb b/source/spec/models/url_spec.rb new file mode 100644 index 0000000..209ca4c --- /dev/null +++ b/source/spec/models/url_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Url, :type => :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/source/spec/models/user_spec.rb b/source/spec/models/user_spec.rb new file mode 100644 index 0000000..47a31bb --- /dev/null +++ b/source/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 diff --git a/source/spec/requests/urls_spec.rb b/source/spec/requests/urls_spec.rb new file mode 100644 index 0000000..cd1976c --- /dev/null +++ b/source/spec/requests/urls_spec.rb @@ -0,0 +1,10 @@ +require 'rails_helper' + +RSpec.describe "Urls", :type => :request do + describe "GET /urls" do + it "works! (now write some real specs)" do + get urls_path + expect(response).to have_http_status(200) + end + end +end diff --git a/source/spec/routing/urls_routing_spec.rb b/source/spec/routing/urls_routing_spec.rb new file mode 100644 index 0000000..d4ec06f --- /dev/null +++ b/source/spec/routing/urls_routing_spec.rb @@ -0,0 +1,35 @@ +require "rails_helper" + +RSpec.describe UrlsController, :type => :routing do + describe "routing" do + + it "routes to #index" do + expect(:get => "/urls").to route_to("urls#index") + end + + it "routes to #new" do + expect(:get => "/urls/new").to route_to("urls#new") + end + + it "routes to #show" do + expect(:get => "/urls/1").to route_to("urls#show", :id => "1") + end + + it "routes to #edit" do + expect(:get => "/urls/1/edit").to route_to("urls#edit", :id => "1") + end + + it "routes to #create" do + expect(:post => "/urls").to route_to("urls#create") + end + + it "routes to #update" do + expect(:put => "/urls/1").to route_to("urls#update", :id => "1") + end + + it "routes to #destroy" do + expect(:delete => "/urls/1").to route_to("urls#destroy", :id => "1") + end + + end +end diff --git a/source/spec/views/sessions/new.html.erb_spec.rb b/source/spec/views/sessions/new.html.erb_spec.rb new file mode 100644 index 0000000..6de37da --- /dev/null +++ b/source/spec/views/sessions/new.html.erb_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe "sessions/new.html.erb", type: :view do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/source/spec/views/urls/edit.html.erb_spec.rb b/source/spec/views/urls/edit.html.erb_spec.rb new file mode 100644 index 0000000..2d8b6a0 --- /dev/null +++ b/source/spec/views/urls/edit.html.erb_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe "urls/edit", :type => :view do + before(:each) do + @url = assign(:url, Url.create!()) + end + + it "renders the edit url form" do + render + + assert_select "form[action=?][method=?]", url_path(@url), "post" do + end + end +end diff --git a/source/spec/views/urls/index.html.erb_spec.rb b/source/spec/views/urls/index.html.erb_spec.rb new file mode 100644 index 0000000..37fdea5 --- /dev/null +++ b/source/spec/views/urls/index.html.erb_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe "urls/index", :type => :view do + before(:each) do + assign(:urls, [ + Url.create!(), + Url.create!() + ]) + end + + it "renders a list of urls" do + render + end +end diff --git a/source/spec/views/urls/new.html.erb_spec.rb b/source/spec/views/urls/new.html.erb_spec.rb new file mode 100644 index 0000000..e614f23 --- /dev/null +++ b/source/spec/views/urls/new.html.erb_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe "urls/new", :type => :view do + before(:each) do + assign(:url, Url.new()) + end + + it "renders new url form" do + render + + assert_select "form[action=?][method=?]", urls_path, "post" do + end + end +end diff --git a/source/spec/views/urls/show.html.erb_spec.rb b/source/spec/views/urls/show.html.erb_spec.rb new file mode 100644 index 0000000..bcd8332 --- /dev/null +++ b/source/spec/views/urls/show.html.erb_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper' + +RSpec.describe "urls/show", :type => :view do + before(:each) do + @url = assign(:url, Url.create!()) + end + + it "renders attributes in

" do + render + end +end diff --git a/source/spec/views/users/new.html.erb_spec.rb b/source/spec/views/users/new.html.erb_spec.rb new file mode 100644 index 0000000..47b47d3 --- /dev/null +++ b/source/spec/views/users/new.html.erb_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe "users/new.html.erb", type: :view do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/source/test/models/user_test.rb b/source/test/models/user_test.rb new file mode 100644 index 0000000..e2fa244 --- /dev/null +++ b/source/test/models/user_test.rb @@ -0,0 +1,55 @@ +require 'test_helper' + +class UserTest < ActiveSupport::TestCase + + def setup + @user = User.new(email: "user@example.com", password: "foobar", password_confirmation: "foobar") + end + + test "should be valid" do + assert @user.valid? + end + + test "email should be present" do + @user.email = "" + assert_not @user.valid? + end + + test "email should not be too long" do + @user.email = "a" * 244 + "@example.com" + assert_not @user.valid? + end + + test "email validation should accept valid addresses" do + valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org first.last@foo.jp alice+bob@baz.cn] + valid_addresses.each do |valid_address| + @user.email = valid_address + assert @user.valid?, '#{valid_address.inspect} should be valid' + end + end + + test "email validation should reject invalid addresses" do + invalid_addresses = %w[user@example,com user_at_foo.org user.name@example. foo@bar_baz.com foo@bar+baz.com] + invalid_addresses.each do |invalid_address| + @user.email = invalid_address + assert_not @user.valid?, "#{invalid_address.inspect} should be invalid" + end + end + + test "email addresses should be unique" do + duplicate_user = @user.dup + duplicate_user.email = @user.email.upcase + @user.save + assert_not duplicate_user.valid? + end + + test "password should be present (nonblank)" do + @user.password = @user.password_confirmation = " " * 5 + assert_not @user.valid? + end + + test "password should have a minimum length" do + @user.password = @user.password_confirmation = "a" * 5 + assert_not @user.valid? + end +end