From bdd77e95e2471acc0022a61110dc48162d8eba84 Mon Sep 17 00:00:00 2001 From: Blair Anderson Date: Mon, 16 Feb 2015 12:38:33 -0800 Subject: [PATCH] application --- Gemfile | 2 + Gemfile.lock | 4 + app/assets/javascripts/application.js | 16 +++ app/assets/javascripts/item_comments.js | 2 + app/assets/javascripts/items.js | 2 + app/assets/javascripts/user_sessions.js | 2 + app/assets/javascripts/users.js | 2 + app/assets/stylesheets/application.css | 15 +++ app/assets/stylesheets/item_comments.scss | 3 + app/assets/stylesheets/items.scss | 3 + app/assets/stylesheets/scaffolds.scss | 69 +++++++++++ app/assets/stylesheets/user_sessions.scss | 3 + app/assets/stylesheets/users.scss | 3 + app/controllers/application_controller.rb | 10 ++ app/controllers/item_comments_controller.rb | 33 +++++ app/controllers/items_controller.rb | 73 +++++++++++ app/controllers/user_item_votes_controller.rb | 39 ++++++ app/controllers/user_sessions_controller.rb | 25 ++++ app/controllers/users_controller.rb | 59 +++++++++ app/helpers/application_helper.rb | 2 + app/helpers/item_comments_helper.rb | 2 + app/helpers/items_helper.rb | 2 + app/helpers/user_item_votes_helper.rb | 16 +++ app/helpers/user_sessions_helper.rb | 2 + app/helpers/users_helper.rb | 2 + app/models/item.rb | 5 + app/models/item_comment.rb | 5 + app/models/user.rb | 21 ++++ app/models/vote.rb | 30 +++++ app/views/items/_form.html.erb | 12 ++ app/views/items/edit.html.erb | 6 + app/views/items/index.html.erb | 14 +++ app/views/items/new.html.erb | 5 + app/views/items/show.html.erb | 34 +++++ app/views/layouts/application.html.erb | 37 ++++++ app/views/user_sessions/_form.html.erb | 11 ++ app/views/user_sessions/create.html.erb | 2 + app/views/user_sessions/destroy.html.erb | 2 + app/views/user_sessions/new.html.erb | 3 + app/views/users/_form.html.erb | 19 +++ app/views/users/edit.html.erb | 6 + app/views/users/index.html.erb | 6 + app/views/users/new.html.erb | 5 + app/views/users/show.html.erb | 16 +++ client/ember_client/.gitkeep | 0 client/react_client/.gitkeep | 0 config/initializers/swagger.rb | 23 ++++ config/routes.rb | 4 +- db/migrate/20150215221519_create_users.rb | 23 ++++ db/migrate/20150215230703_create_items.rb | 19 +++ db/migrate/20150216000309_create_votes.rb | 13 ++ .../20150216170958_create_item_comments.rb | 11 ++ db/schema.rb | 74 +++++++++++ doc/api/index.html | 117 ++++++++++++++++++ public/api/v1/api-docs.json | 19 +++ public/api/v1/items.json | 19 +++ spec/acceptance/users_spec.rb | 21 ++++ spec/factories/users.rb | 10 ++ test/models/item_comment_test.rb | 7 ++ test/models/item_test.rb | 7 ++ test/models/user_test.rb | 7 ++ test/models/vote_test.rb | 7 ++ 62 files changed, 1009 insertions(+), 2 deletions(-) create mode 100644 app/assets/javascripts/application.js create mode 100644 app/assets/javascripts/item_comments.js create mode 100644 app/assets/javascripts/items.js create mode 100644 app/assets/javascripts/user_sessions.js create mode 100644 app/assets/javascripts/users.js create mode 100644 app/assets/stylesheets/application.css create mode 100644 app/assets/stylesheets/item_comments.scss create mode 100644 app/assets/stylesheets/items.scss create mode 100644 app/assets/stylesheets/scaffolds.scss create mode 100644 app/assets/stylesheets/user_sessions.scss create mode 100644 app/assets/stylesheets/users.scss create mode 100644 app/controllers/application_controller.rb create mode 100644 app/controllers/item_comments_controller.rb create mode 100644 app/controllers/items_controller.rb create mode 100644 app/controllers/user_item_votes_controller.rb create mode 100644 app/controllers/user_sessions_controller.rb create mode 100644 app/controllers/users_controller.rb create mode 100644 app/helpers/application_helper.rb create mode 100644 app/helpers/item_comments_helper.rb create mode 100644 app/helpers/items_helper.rb create mode 100644 app/helpers/user_item_votes_helper.rb create mode 100644 app/helpers/user_sessions_helper.rb create mode 100644 app/helpers/users_helper.rb create mode 100644 app/models/item.rb create mode 100644 app/models/item_comment.rb create mode 100644 app/models/user.rb create mode 100644 app/models/vote.rb create mode 100644 app/views/items/_form.html.erb create mode 100644 app/views/items/edit.html.erb create mode 100644 app/views/items/index.html.erb create mode 100644 app/views/items/new.html.erb create mode 100644 app/views/items/show.html.erb create mode 100644 app/views/layouts/application.html.erb create mode 100644 app/views/user_sessions/_form.html.erb create mode 100644 app/views/user_sessions/create.html.erb create mode 100644 app/views/user_sessions/destroy.html.erb create mode 100644 app/views/user_sessions/new.html.erb create mode 100644 app/views/users/_form.html.erb create mode 100644 app/views/users/edit.html.erb create mode 100644 app/views/users/index.html.erb create mode 100644 app/views/users/new.html.erb create mode 100644 app/views/users/show.html.erb create mode 100644 client/ember_client/.gitkeep create mode 100644 client/react_client/.gitkeep create mode 100644 config/initializers/swagger.rb create mode 100644 db/migrate/20150215221519_create_users.rb create mode 100644 db/migrate/20150215230703_create_items.rb create mode 100644 db/migrate/20150216000309_create_votes.rb create mode 100644 db/migrate/20150216170958_create_item_comments.rb create mode 100644 db/schema.rb create mode 100644 doc/api/index.html create mode 100644 public/api/v1/api-docs.json create mode 100644 public/api/v1/items.json create mode 100644 spec/acceptance/users_spec.rb create mode 100644 spec/factories/users.rb create mode 100644 test/models/item_comment_test.rb create mode 100644 test/models/item_test.rb create mode 100644 test/models/user_test.rb create mode 100644 test/models/vote_test.rb diff --git a/Gemfile b/Gemfile index 724ac9f..b254470 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,8 @@ group :development, :test do # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' + gem 'swagger-docs' + gem 'factory_girl_rails' gem 'faker' gem 'rspec-rails', '~> 3.0' # https://github.com/rspec/rspec-rails diff --git a/Gemfile.lock b/Gemfile.lock index 18ccee4..f968838 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -176,6 +176,9 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) + swagger-docs (0.1.9) + activesupport (>= 3, < 5) + rails (>= 3, < 5) thor (0.19.1) thread_safe (0.3.4) tilt (1.4.1) @@ -208,6 +211,7 @@ DEPENDENCIES simple_form sorcery (= 0.9.0) spring + swagger-docs turbolinks uglifier (>= 1.3.0) web-console (~> 2.0) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js new file mode 100644 index 0000000..4a2ae1d --- /dev/null +++ b/app/assets/javascripts/application.js @@ -0,0 +1,16 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. +// +// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details +// about supported directives. +// +//= require jquery +//= require jquery_ujs +//= require turbolinks +//= require_tree . diff --git a/app/assets/javascripts/item_comments.js b/app/assets/javascripts/item_comments.js new file mode 100644 index 0000000..dee720f --- /dev/null +++ b/app/assets/javascripts/item_comments.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/javascripts/items.js b/app/assets/javascripts/items.js new file mode 100644 index 0000000..dee720f --- /dev/null +++ b/app/assets/javascripts/items.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/javascripts/user_sessions.js b/app/assets/javascripts/user_sessions.js new file mode 100644 index 0000000..dee720f --- /dev/null +++ b/app/assets/javascripts/user_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/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/application.css b/app/assets/stylesheets/application.css new file mode 100644 index 0000000..f9cd5b3 --- /dev/null +++ b/app/assets/stylesheets/application.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any styles + * defined in the other CSS/SCSS files in this directory. It is generally better to create a new + * file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/app/assets/stylesheets/item_comments.scss b/app/assets/stylesheets/item_comments.scss new file mode 100644 index 0000000..0bb9ea2 --- /dev/null +++ b/app/assets/stylesheets/item_comments.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the item_comments controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/items.scss b/app/assets/stylesheets/items.scss new file mode 100644 index 0000000..49e8648 --- /dev/null +++ b/app/assets/stylesheets/items.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the items controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/scaffolds.scss b/app/assets/stylesheets/scaffolds.scss new file mode 100644 index 0000000..6ec6a8f --- /dev/null +++ b/app/assets/stylesheets/scaffolds.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/app/assets/stylesheets/user_sessions.scss b/app/assets/stylesheets/user_sessions.scss new file mode 100644 index 0000000..69017e2 --- /dev/null +++ b/app/assets/stylesheets/user_sessions.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the UserSessions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/users.scss b/app/assets/stylesheets/users.scss new file mode 100644 index 0000000..1efc835 --- /dev/null +++ b/app/assets/stylesheets/users.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the users controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 0000000..d57fac6 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,10 @@ +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 not_authenticated + redirect_to login_path, alert: "Please login first" + end +end diff --git a/app/controllers/item_comments_controller.rb b/app/controllers/item_comments_controller.rb new file mode 100644 index 0000000..51bf898 --- /dev/null +++ b/app/controllers/item_comments_controller.rb @@ -0,0 +1,33 @@ +class ItemCommentsController < ApplicationController + before_action :set_item + + def index + @comments = @item.comments.order(created_at: :asc) + end + + def create + @comment = current_user.item_comments.build(comment_params) + if @comment.save + redirect_back_or_to item_path(@item), notice: "Success." + else + redirect_back_or_to item_path(@item), notice: @comment.errors.full_messages.join(". ") + end + end + + def update + end + + def destroy + end + + private + + def set_item + @item = Item.find(params[:item_id]) + return redirect_back_or_to root_path, notice: 'could not find item' unless @item + end + + def comment_params + params.require(:item_comment).permit(:content).merge({item_id: params[:item_id]}) + end +end diff --git a/app/controllers/items_controller.rb b/app/controllers/items_controller.rb new file mode 100644 index 0000000..ce14c2a --- /dev/null +++ b/app/controllers/items_controller.rb @@ -0,0 +1,73 @@ +class ItemsController < ApplicationController + before_action :require_login, only: [:new, :create, :edit, :update, :toggle] + before_action :set_item, only: [:show] + before_action :set_user_item, only: [:edit, :update, :toggle] + + swagger_controller :items, "Item Management" + + swagger_api :index do + summary "Fetches all Current items" + notes "This lists items" + # param :query, :page, :integer, :optional, "Page number" + # param :path, :nested_id, :integer, :optional, "Team Id" + # response :unauthorized + # response :not_acceptable, "The request you made is not acceptable" + # response :requested_range_not_satisfiable + end + def index + @items = Item.order(score: :desc).includes(:user) + end + + def show + @comments = @item.comments.includes(:user).order(created_at: :asc) + end + + # GET /items/new + def new + @item = Item.new + end + + def edit + end + + def create + @item = current_user.items.build(item_params) + + if @item.save + redirect_to @item, notice: 'Item was successfully created.' + else + render :new + end + end + + def update + if @item.update(item_params) + redirect_to @item, notice: 'Item was successfully updated.' + else + render :edit + end + end + + def toggle + @item.update(:disabled, @item.disabled?) + message = item.disabled? ? 'disabled' : 'enabled' + redirect_to @item, notice: "Item #{message}." + end + + private + def set_item + @item = Item.find(params[:id]) + end + + def set_user_item + @item = current_user.items.find(params[:id]) + return redirect_to :back, notice: 'Unauthorized' unless @item + end + + def item_params + params.require(:item).permit( + :title, + :url + ) + end +end diff --git a/app/controllers/user_item_votes_controller.rb b/app/controllers/user_item_votes_controller.rb new file mode 100644 index 0000000..305fc79 --- /dev/null +++ b/app/controllers/user_item_votes_controller.rb @@ -0,0 +1,39 @@ +class UserItemVotesController < ApplicationController + before_action :set_item + + def create + vote = current_user.votes.build(vote_params) + byebug + if vote.save + redirect_to :back, notice: "Upvoted." + else + redirect_to :back, notice: "Already Upvoted." + end + end + + def destroy + vote = current_user.votes.where(vote_params).first + if vote + vote.destroy + message = 'Removed Vote.' + else + message = 'No vote to remove.' + end + redirect_to :back, notice: message + end + + private + def set_item + @item = Item.find(params[:id]) + unless @item + return redirect_to :back, notice: "Could not find item with #{params[:id]}" + end + end + + def vote_params + { + votable_id: @item.id, + votable_type: @item.class.to_s + } + end +end diff --git a/app/controllers/user_sessions_controller.rb b/app/controllers/user_sessions_controller.rb new file mode 100644 index 0000000..a007d4d --- /dev/null +++ b/app/controllers/user_sessions_controller.rb @@ -0,0 +1,25 @@ +class UserSessionsController < ApplicationController + before_action :require_login, only: [:destroy] + def new + @user = User.new + end + + def create + if @user = login(login_params[:username], login_params[:password]) + redirect_back_or_to(:users, notice: 'Login successful') + else + flash.now[:alert] = 'Login failed' + render action: 'new' + end + end + + def destroy + logout + redirect_to(:users, notice: 'Logged out!') + end + + private + def login_params + params.require(:login).permit(:username, :password) + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..2148b7e --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,59 @@ +class UsersController < ApplicationController + before_action :require_login, only: [:edit, :update, :destroy] + before_action :set_user, only: [:show, :edit, :update, :destroy] + + def new + # user registration page + @user = User.new + end + + def create + @user = User.new(user_params) + + if @user.save + auto_login(@user) + redirect_to root_path, notice: 'User was successfully created.' + else + render :new + end + end + + def show + # profile page + end + + def edit + # profile edit + end + + def update + if @user.update(user_params) + redirect_to @user, notice: 'User was successfully updated.' + else + render :edit + end + end + + def destroy + @user.destroy + redirect_to root_path, notice: 'User was successfully destroyed.' + end + + private + def set_user + @user = User.where(username: params[:id]).first + unless @user + return redirect_back_or_to root_path, notice: 'could not find user' + end + end + + # Only allow a trusted parameter "white list" through. + def user_params + params.require(:user).permit( + :username, + :password, + :password_confirmation, + :about + ) + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 0000000..de6be79 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/app/helpers/item_comments_helper.rb b/app/helpers/item_comments_helper.rb new file mode 100644 index 0000000..aec7b02 --- /dev/null +++ b/app/helpers/item_comments_helper.rb @@ -0,0 +1,2 @@ +module ItemCommentsHelper +end diff --git a/app/helpers/items_helper.rb b/app/helpers/items_helper.rb new file mode 100644 index 0000000..cff0c9f --- /dev/null +++ b/app/helpers/items_helper.rb @@ -0,0 +1,2 @@ +module ItemsHelper +end diff --git a/app/helpers/user_item_votes_helper.rb b/app/helpers/user_item_votes_helper.rb new file mode 100644 index 0000000..7f83a2c --- /dev/null +++ b/app/helpers/user_item_votes_helper.rb @@ -0,0 +1,16 @@ +module UserItemVotesHelper + def link_to_upvote(object) + link_to 'up', vote_item_path(object), method: :post + end + + def link_to_downvote(object) + link_to 'down', vote_item_path(object), method: :delete + end + + def render_votes_for_item(item) + [ + link_to_upvote(item), + link_to_downvote(item) + ].join('/').html_safe + end +end diff --git a/app/helpers/user_sessions_helper.rb b/app/helpers/user_sessions_helper.rb new file mode 100644 index 0000000..2018402 --- /dev/null +++ b/app/helpers/user_sessions_helper.rb @@ -0,0 +1,2 @@ +module UserSessionsHelper +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/item.rb b/app/models/item.rb new file mode 100644 index 0000000..119d659 --- /dev/null +++ b/app/models/item.rb @@ -0,0 +1,5 @@ +class Item < ActiveRecord::Base + belongs_to :user + has_many :votes, as: :votable + has_many :comments, class_name: "ItemComment" +end diff --git a/app/models/item_comment.rb b/app/models/item_comment.rb new file mode 100644 index 0000000..7ad936e --- /dev/null +++ b/app/models/item_comment.rb @@ -0,0 +1,5 @@ +class ItemComment < ActiveRecord::Base + belongs_to :user + belongs_to :item, counter_cache: :comments_count + validates_presence_of :user_id, :item_id, :content +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..1289c26 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,21 @@ +class User < ActiveRecord::Base + authenticates_with_sorcery! + + validates :password, length: { minimum: 5 } + validates :password, confirmation: true + validates :password_confirmation, presence: true + + validates :username, uniqueness: true, length: {minimum: 2} + + has_many :items + has_many :votes + has_many :item_comments + + def item_votes + votes.where(votable_type: "Item") + end + + def to_param + username.downcase + end +end diff --git a/app/models/vote.rb b/app/models/vote.rb new file mode 100644 index 0000000..ae7f5c0 --- /dev/null +++ b/app/models/vote.rb @@ -0,0 +1,30 @@ +class Vote < ActiveRecord::Base + belongs_to :votable, polymorphic: true + belongs_to :user + + validates_uniqueness_of :votable_id, scope: [:user_id, :votable_type] + + after_create do + update_votes + end + + after_destroy do + update_votes + end + + def get_object + return @object if @object && @object.valid? + klass = votable_type.constantize + return false unless klass.exists? + @object = klass.find(votable_id) + end + + def update_votes + attrs = self.attributes.with_indifferent_access.slice(:votable_id, :votable_type) + count = Vote.where(attrs).count + get_object.update( + upvotes_count: count, + score: count + ) + end +end diff --git a/app/views/items/_form.html.erb b/app/views/items/_form.html.erb new file mode 100644 index 0000000..9689ec7 --- /dev/null +++ b/app/views/items/_form.html.erb @@ -0,0 +1,12 @@ +<%= simple_form_for(@item) do |f| %> + <%= f.error_notification %> + +
+ <%= f.input :title %> + <%= f.input :url %> +
+ +
+ <%= f.button :submit %> +
+<% end %> diff --git a/app/views/items/edit.html.erb b/app/views/items/edit.html.erb new file mode 100644 index 0000000..d24e6a3 --- /dev/null +++ b/app/views/items/edit.html.erb @@ -0,0 +1,6 @@ +

Editing Item

+ +<%= render 'form' %> + +<%= link_to 'Show', @item %> | +<%= link_to 'Back', items_path %> diff --git a/app/views/items/index.html.erb b/app/views/items/index.html.erb new file mode 100644 index 0000000..5a1f09d --- /dev/null +++ b/app/views/items/index.html.erb @@ -0,0 +1,14 @@ +

Items

+ diff --git a/app/views/items/new.html.erb b/app/views/items/new.html.erb new file mode 100644 index 0000000..1c8e2f4 --- /dev/null +++ b/app/views/items/new.html.erb @@ -0,0 +1,5 @@ +

New Item

+ +<%= render 'form' %> + +<%= link_to 'Back', items_path %> diff --git a/app/views/items/show.html.erb b/app/views/items/show.html.erb new file mode 100644 index 0000000..750209e --- /dev/null +++ b/app/views/items/show.html.erb @@ -0,0 +1,34 @@ +

+ [score: <%= @item.score %>] + <%= render_votes_for_item(@item) %> + comments: <%= @item.comments_count %> +

+ +

+ + <%= link_to @item.title, @item, title: @item.url %> + (<%= @item.url %>) + + (posted by: <%= link_to @item.user.username, @item.user %>) +

+ + diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb new file mode 100644 index 0000000..6364d74 --- /dev/null +++ b/app/views/layouts/application.html.erb @@ -0,0 +1,37 @@ + + + + RailsHackernewsRedditProducthunt + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> + <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> + <%= csrf_meta_tags %> + + + +<% if current_user %> +
+ <%= current_user.username %> +
+<% end %> +
+

<%= flash[:notice] %>

+ +

<%= flash[:alert] %>

+
+<%= yield %> + + + diff --git a/app/views/user_sessions/_form.html.erb b/app/views/user_sessions/_form.html.erb new file mode 100644 index 0000000..6a1cef8 --- /dev/null +++ b/app/views/user_sessions/_form.html.erb @@ -0,0 +1,11 @@ +<%= simple_form_for :login, url: user_sessions_path, method: :post do |f| %> + +
+ <%= f.input :username %> + <%= f.input :password %> +
+
+ <%= f.button :submit, "Login" %> +
+ +<% end %> diff --git a/app/views/user_sessions/create.html.erb b/app/views/user_sessions/create.html.erb new file mode 100644 index 0000000..b25f738 --- /dev/null +++ b/app/views/user_sessions/create.html.erb @@ -0,0 +1,2 @@ +

UserSessions#create

+

Find me in app/views/user_sessions/create.html.erb

diff --git a/app/views/user_sessions/destroy.html.erb b/app/views/user_sessions/destroy.html.erb new file mode 100644 index 0000000..50ebdef --- /dev/null +++ b/app/views/user_sessions/destroy.html.erb @@ -0,0 +1,2 @@ +

UserSessions#destroy

+

Find me in app/views/user_sessions/destroy.html.erb

diff --git a/app/views/user_sessions/new.html.erb b/app/views/user_sessions/new.html.erb new file mode 100644 index 0000000..8527a02 --- /dev/null +++ b/app/views/user_sessions/new.html.erb @@ -0,0 +1,3 @@ +

Login

+ +<%= render 'form' %> diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb new file mode 100644 index 0000000..d4cf29b --- /dev/null +++ b/app/views/users/_form.html.erb @@ -0,0 +1,19 @@ +<%= simple_form_for(@user) do |f| %> + <%= f.error_notification %> + +
+ <%= f.input :username %> + <%= f.input :password, as: :password %> + <%= f.input :password_confirmation, as: :password %> +
+ +
+ <%= f.input :about %> +
+ +
+ <%= f.button :submit %> +
+<% end %> + + diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb new file mode 100644 index 0000000..d87b2f5 --- /dev/null +++ b/app/views/users/edit.html.erb @@ -0,0 +1,6 @@ +

Editing User

+ +<%= render 'form' %> + +<%= link_to 'Show', @user %> | +<%= link_to 'Back', users_path %> diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb new file mode 100644 index 0000000..40635e0 --- /dev/null +++ b/app/views/users/index.html.erb @@ -0,0 +1,6 @@ +

Listing Users

+ diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb new file mode 100644 index 0000000..4854d9e --- /dev/null +++ b/app/views/users/new.html.erb @@ -0,0 +1,5 @@ +

New User

+ +<%= render 'form' %> + +<%= link_to 'Back', users_path %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb new file mode 100644 index 0000000..2de6b97 --- /dev/null +++ b/app/views/users/show.html.erb @@ -0,0 +1,16 @@ +

+ Username: + <%= @user.username %> +

+ + +

+ Karma: + <%= @user.karma %> +

+ + +

+ About: + <%= @user.about %> +

diff --git a/client/ember_client/.gitkeep b/client/ember_client/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/client/react_client/.gitkeep b/client/react_client/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/config/initializers/swagger.rb b/config/initializers/swagger.rb new file mode 100644 index 0000000..1a6fc97 --- /dev/null +++ b/config/initializers/swagger.rb @@ -0,0 +1,23 @@ +v1 = { + # the extension used for the API + api_extension_type: :json, + # the output location where your .json files are written to + api_file_path: "public/api/v1/", + # the URL base path to your API + base_path: "http://api.somedomain.com", + # if you want to delete all .json files at each generation + clean_directory: false, + # add custom attributes to api-docs + attributes: { + info: { + "title" => "Swagger Sample App", + "description" => "This is a sample description.", + "termsOfServiceUrl" => "http://helloreverb.com/terms/", + "contact" => "apiteam@wordnik.com", + "license" => "Apache 2.0", + "licenseUrl" => "http://www.apache.org/licenses/LICENSE-2.0.html" + } + } +} + +Swagger::Docs::Config.register_apis({"1.0" => v1}) diff --git a/config/routes.rb b/config/routes.rb index a53ffbd..920d545 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ Rails.application.routes.draw do root :to => 'items#index' - resources :user_sessions - resources :users + resources :user_sessions, only: [:new, :create, :destroy] + resources :users, except: [:index] resources :items, except: [:destroy] do resources :item_comments member do diff --git a/db/migrate/20150215221519_create_users.rb b/db/migrate/20150215221519_create_users.rb new file mode 100644 index 0000000..989435f --- /dev/null +++ b/db/migrate/20150215221519_create_users.rb @@ -0,0 +1,23 @@ +class CreateUsers < ActiveRecord::Migration + def change + create_table :users do |t| + t.string :username, null: false + t.string :crypted_password + t.string :salt + t.boolean :admin, default: false, null: false + t.boolean :disabled, default: false, null: false + t.integer :karma, default: 0, null: false + t.text :about + t.string :auth + t.string :api_seceret + t.datetime :karma_increment_time + t.datetime :pwd_reset + t.integer :replies_count, default: 0, null: false + + t.timestamps + end + + add_index :users, :username, unique: true + add_index :users, [:auth , :api_seceret] + end +end diff --git a/db/migrate/20150215230703_create_items.rb b/db/migrate/20150215230703_create_items.rb new file mode 100644 index 0000000..e73bbb5 --- /dev/null +++ b/db/migrate/20150215230703_create_items.rb @@ -0,0 +1,19 @@ +class CreateItems < ActiveRecord::Migration + def change + create_table :items do |t| + t.string :title, null: false + t.string :url + t.integer :user_id, null: false + t.boolean :disabled, default: false, null: false + t.integer :comments_count, default: 0, null: false + t.integer :upvotes_count, default: 0, null: false + t.integer :downvotes_count, default: 0, null: false + t.integer :score, default: 0, null: false + t.integer :rank, default: 0, null: false + + t.timestamps null: false + end + add_index :items, :user_id + add_index :items, :disabled + end +end diff --git a/db/migrate/20150216000309_create_votes.rb b/db/migrate/20150216000309_create_votes.rb new file mode 100644 index 0000000..32ad593 --- /dev/null +++ b/db/migrate/20150216000309_create_votes.rb @@ -0,0 +1,13 @@ +class CreateVotes < ActiveRecord::Migration + def change + create_table :votes do |t| + t.integer :user_id, null: false + t.integer :votable_id, null: false + t.string :votable_type, null: false + + t.timestamps null: false + end + + add_index :votes, [:user_id, :votable_id, :votable_type], unique: true + end +end diff --git a/db/migrate/20150216170958_create_item_comments.rb b/db/migrate/20150216170958_create_item_comments.rb new file mode 100644 index 0000000..6cb1174 --- /dev/null +++ b/db/migrate/20150216170958_create_item_comments.rb @@ -0,0 +1,11 @@ +class CreateItemComments < ActiveRecord::Migration + def change + create_table :item_comments do |t| + t.integer :user_id, null: false + t.integer :item_id, null: false + t.text :content, null: false + + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..b078e4a --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,74 @@ +# 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: 20150216170958) do + + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "item_comments", force: :cascade do |t| + t.integer "user_id", null: false + t.integer "item_id", null: false + t.text "content", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "items", force: :cascade do |t| + t.string "title", null: false + t.string "url" + t.integer "user_id", null: false + t.boolean "disabled", default: false, null: false + t.integer "comments_count", default: 0, null: false + t.integer "upvotes_count", default: 0, null: false + t.integer "downvotes_count", default: 0, null: false + t.integer "score", default: 0, null: false + t.integer "rank", default: 0, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "items", ["disabled"], name: "index_items_on_disabled", using: :btree + add_index "items", ["user_id"], name: "index_items_on_user_id", using: :btree + + create_table "users", force: :cascade do |t| + t.string "username", null: false + t.string "crypted_password" + t.string "salt" + t.boolean "admin", default: false, null: false + t.boolean "disabled", default: false, null: false + t.integer "karma", default: 0, null: false + t.text "about" + t.string "auth" + t.string "api_seceret" + t.datetime "karma_increment_time" + t.datetime "pwd_reset" + t.integer "replies_count", default: 0, null: false + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "users", ["auth", "api_seceret"], name: "index_users_on_auth_and_api_seceret", using: :btree + add_index "users", ["username"], name: "index_users_on_username", unique: true, using: :btree + + create_table "votes", force: :cascade do |t| + t.integer "user_id", null: false + t.integer "votable_id", null: false + t.string "votable_type", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "votes", ["user_id", "votable_id", "votable_type"], name: "index_votes_on_user_id_and_votable_id_and_votable_type", unique: true, using: :btree + +end diff --git a/doc/api/index.html b/doc/api/index.html new file mode 100644 index 0000000..6fddd5d --- /dev/null +++ b/doc/api/index.html @@ -0,0 +1,117 @@ + + + + API Documentation + + + + +
+

API Documentation

+ +
+ + diff --git a/public/api/v1/api-docs.json b/public/api/v1/api-docs.json new file mode 100644 index 0000000..de8601b --- /dev/null +++ b/public/api/v1/api-docs.json @@ -0,0 +1,19 @@ +{ + "apiVersion": "1.0", + "swaggerVersion": "1.2", + "basePath": "http://api.somedomain.com/", + "apis": [ + { + "path": "items.{format}", + "description": "Item Management" + } + ], + "info": { + "title": "Swagger Sample App", + "description": "This is a sample description.", + "termsOfServiceUrl": "http://helloreverb.com/terms/", + "contact": "apiteam@wordnik.com", + "license": "Apache 2.0", + "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html" + } +} \ No newline at end of file diff --git a/public/api/v1/items.json b/public/api/v1/items.json new file mode 100644 index 0000000..1bd3229 --- /dev/null +++ b/public/api/v1/items.json @@ -0,0 +1,19 @@ +{ + "apiVersion": "1.0", + "swaggerVersion": "1.2", + "basePath": "http://api.somedomain.com/", + "resourcePath": "items", + "apis": [ + { + "path": "", + "operations": [ + { + "summary": "Fetches all Current items", + "notes": "This lists items", + "method": "get", + "nickname": "Items#index" + } + ] + } + ] +} \ No newline at end of file diff --git a/spec/acceptance/users_spec.rb b/spec/acceptance/users_spec.rb new file mode 100644 index 0000000..6ebb9ee --- /dev/null +++ b/spec/acceptance/users_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' +require 'rspec_api_documentation/dsl' + +resource "users" do + get "/user" do + example "Current User Resource" do + user = FactoryGirl.create(:user, password: "password", password_confirmation: "password") + login_user_post(user.username, "password") + get "/user" + status.should == 200 + end + end + + get "/user/:username" do + example "User Resource" do + user = FactoryGirl.create(:user) + get "users/#{user.username}" + status.should == 200 + end + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb new file mode 100644 index 0000000..9cb67d5 --- /dev/null +++ b/spec/factories/users.rb @@ -0,0 +1,10 @@ +FactoryGirl.define do + factory :user do + username { Faker::Internet.user_name(Faker::Name.name) } + password "password" + password_confirmation "password" + trait :admin do + admin true + end + end +end diff --git a/test/models/item_comment_test.rb b/test/models/item_comment_test.rb new file mode 100644 index 0000000..d774993 --- /dev/null +++ b/test/models/item_comment_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class ItemCommentTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/item_test.rb b/test/models/item_test.rb new file mode 100644 index 0000000..f564d81 --- /dev/null +++ b/test/models/item_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class ItemTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/user_test.rb b/test/models/user_test.rb new file mode 100644 index 0000000..82f61e0 --- /dev/null +++ b/test/models/user_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class UserTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/vote_test.rb b/test/models/vote_test.rb new file mode 100644 index 0000000..f31f992 --- /dev/null +++ b/test/models/vote_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class VoteTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end