[refs #54] Base logic and templates for issues and comments to them

This commit is contained in:
konstantin.grabar 2011-12-19 19:30:14 +04:00
parent df61ee8459
commit d20d43eb2b
31 changed files with 430 additions and 1 deletions

View File

@ -22,6 +22,7 @@ gem 'paperclip', "~> 2.3"
gem "will_paginate", "~> 3.0.2" gem "will_paginate", "~> 3.0.2"
gem 'meta-tags', '~> 1.2.4', :require => 'meta_tags' gem 'meta-tags', '~> 1.2.4', :require => 'meta_tags'
gem "russian" gem "russian"
gem "friendly_id", "~> 4.0.0.beta14"
# gem 'ghoul_grack', '~> 0.0.1' # gem 'ghoul_grack', '~> 0.0.1'
gem 'grack', :git => 'git://github.com/rdblue/grack.git', :require => 'git_http' gem 'grack', :git => 'git://github.com/rdblue/grack.git', :require => 'git_http'

View File

@ -77,6 +77,7 @@ GEM
factory_girl_rails (1.4.0) factory_girl_rails (1.4.0)
factory_girl (~> 2.3.0) factory_girl (~> 2.3.0)
railties (>= 3.0.0) railties (>= 3.0.0)
friendly_id (4.0.0.beta8)
grit (2.4.1) grit (2.4.1)
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (~> 1.15) mime-types (~> 1.15)
@ -219,6 +220,7 @@ DEPENDENCIES
delayed_job delayed_job
devise (~> 1.5.2) devise (~> 1.5.2)
factory_girl_rails (~> 1.4.0) factory_girl_rails (~> 1.4.0)
friendly_id (~> 4.0.0.beta14)
grack! grack!
grit grit
haml-rails (~> 0.3.4) haml-rails (~> 0.3.4)

View File

@ -0,0 +1,50 @@
class CommentsController < ApplicationController
before_filter :authenticate_user!
def index
@commentable = find_commentable
@comments = @commentable.comments
end
def create
@commentable = find_commentable
@comment = @commentable.comments.build(params[:comment])
if @comment.save
flash[:notice] = I18n.t("flash.comment.saved")
redirect_to :id => nil
else
flash[:error] = I18n.t("flash.comment.saved_error")
render :action => 'new'
end
end
def update
@comment = Comment.find(params[:id])
if @comment.update_attributes(params[:comment])
flash[:notice] = I18n.t("flash.comment.saved")
redirect_to :id => nil
else
flash[:error] = I18n.t("flash.comment.saved_error")
render :action => 'new'
end
end
def destroy
@comment = Comment.find(params[:id])
@comment.destroy
flash[:notice] = t("flash.comment.destroyed")
redirect_to root_path
end
private
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
end

View File

@ -0,0 +1,58 @@
class IssuesController < ApplicationController
before_filter :authenticate_user!
before_filter :find_project
before_filter :find_issue, :only => [:show, :edit, :update, :destroy]
load_and_authorize_resource
autocomplete :user, :uname
def index
@issues = @project.issues.paginate :per_page => 10, :page => params[:page]
end
def create
@user_id = params[:user_id]
@user_uname = params[:user_uname]
@issue = Issue.new(params[:issue])
@issue.user_id = @user_id
@issue.project_id = @project.id
if @issue.save!
flash[:notice] = I18n.t("flash.issue.saved")
redirect_to project_issues_path(@project)
else
flash[:error] = I18n.t("flash.issue.saved_error")
render :action => :new
end
end
def update
@user_id = params[:user_id]
@user_uname = params[:user_uname]
if @issue.update_attributes( params[:issue].merge({:user_id => @user_id}) )
flash[:notice] = I18n.t("flash.issue.saved")
redirect_to @issue
else
flash[:error] = I18n.t("flash.issue.saved_error")
render :action => :new
end
end
def destroy
@issue.destroy
flash[:notice] = t("flash.issue.destroyed")
redirect_to root_path
end
private
def find_project
@project = Project.find(params[:project_id])
end
def find_issue
@issue = Issue.find(params[:id])
end
end

View File

@ -0,0 +1,2 @@
module CommentsHelper
end

View File

@ -0,0 +1,2 @@
module IssuesHelper
end

5
app/models/comment.rb Normal file
View File

@ -0,0 +1,5 @@
class Comment < ActiveRecord::Base
belongs_to :commentable
validates :body, :commentable_id, :commentable_type, :presence => true
end

24
app/models/issue.rb Normal file
View File

@ -0,0 +1,24 @@
class Issue < ActiveRecord::Base
STATUSES = ['open', 'close']
extend FriendlyId
friendly_id :serial_id
belongs_to :project
belongs_to :user
has_many :comments, :as => :commentable
validates :title, :body, :project_id, :user_id, :presence => true
attr_readonly :serial_id
after_create :set_serial_id
protected
def set_serial_id
serial_id = project.issues.count + 1
save
end
end

View File

@ -4,6 +4,7 @@ class Project < ActiveRecord::Base
belongs_to :category, :counter_cache => true belongs_to :category, :counter_cache => true
belongs_to :owner, :polymorphic => true belongs_to :owner, :polymorphic => true
has_many :issues, :dependent => :destroy
has_many :build_lists, :dependent => :destroy has_many :build_lists, :dependent => :destroy
has_many :auto_build_lists, :dependent => :destroy has_many :auto_build_lists, :dependent => :destroy

View File

@ -0,0 +1,10 @@
.group
= f.label :body, :class => :label
= f.text_area :body, :class => 'text_field', :cols => 80
.group.navform.wat-cf
%button.button{:type => "submit"}
= image_tag("web-app-theme/icons/tick.png", :alt => t("layout.save"))
= t("layout.save")
%span.text_button_padding= t("layout.or")
= link_to t("layout.cancel"), project_issue_comment_path(@issue.project, @issue), :class => "text_button_padding link_button"

View File

@ -0,0 +1,10 @@
.block
.secondary-navigation
%ul.wat-cf
%li.first= link_to t("layout.issues.list"), project_issue_path(@project, @issue)
.content
%h2.title
= t("layout.issues.edit_header")
.inner
= form_for @comment, :url => project_issue_comment_path(@project, @issue, @comment), :html => { :class => :form } do |f|
= render :partial => "form", :locals => {:f => f}

View File

@ -0,0 +1,25 @@
= javascript_include_tag "autocomplete-rails.js"
.group
= f.label :title, :class => :label
= f.text_field :title, :class => 'text_field'
.group
= f.label :body, :class => :label
= f.text_area :body, :class => 'text_field', :cols => 80
.group
= f.label :status, :class => :label
= f.select :status, Issue::STATUSES, :class => 'text_field'
.group
= label_tag "", t("layout.issues.user_id"), :class => :label
= autocomplete_field_tag 'user_id', @user_uname, autocomplete_user_uname_platforms_path, :id_element => '#user_id_field'
= hidden_field_tag 'user_id', @user_id, :id => 'user_id_field'
.group.navform.wat-cf
%button.button{:type => "submit"}
= image_tag("web-app-theme/icons/tick.png", :alt => t("layout.save"))
= t("layout.save")
%span.text_button_padding= t("layout.or")
= link_to t("layout.cancel"), project_path(@project), :class => "text_button_padding link_button"

View File

@ -0,0 +1,15 @@
%table.table
%tr
%th.first= t("activerecord.attributes.issue.title")
%th.first= t("activerecord.attributes.issue.status")
%th.last &nbsp;
- @project.issues.each do |issue|
%tr{:class => cycle("odd", "even")}
%td
= link_to issue.title, project_issue_path(@project, issue)
%td
= issue.status
%td.last
= link_to t("layout.show"), project_issue_path(@project, issue)
|
= link_to t("layout.delete"), project_issue_path(@project, issue), :method => :delete, :confirm => t("layout.issues.confirm_delete") if can? :destroy, issue

View File

@ -0,0 +1,12 @@
.block
.secondary-navigation
%ul.wat-cf
%li.first= link_to t("layout.issues.list"), project_issues_path(@project)
%li= link_to t("layout.issues.new"), new_project_issue_path(@project)
%li.active= link_to t("layout.issues.edit"), edit_project_issue_path(@project)
.content
%h2.title
= t("layout.issues.edit_header")
.inner
= form_for @issue, :url => project_issue_path(@issue), :html => { :class => :form } do |f|
= render :partial => "form", :locals => {:f => f}

View File

@ -0,0 +1,14 @@
.block
.secondary-navigation
%ul.wat-cf
%li.first.active= link_to t("layout.issues.list"), project_issues_path(@project)
%li= link_to t("layout.issues.new"), new_project_issue_path(@project) if can? :create, Issue
.content
%h2.title
= t("layout.issues.list_header")
.inner
= render :partial => 'shared/search_form'
= render :partial => 'issues/list', :object => @issues
.actions-bar.wat-cf
.actions
= will_paginate @issues, :param_name => :issue_page

View File

@ -0,0 +1,11 @@
.block
.secondary-navigation
%ul.wat-cf
%li.first= link_to "#{t("layout.issues.list")}", project_issues_path(@project)
%li.active= link_to "#{t("layout.issues.new")}", new_project_issue_path(@project)
.content
%h2.title
= t("layout.issues.new_header")
.inner
= form_for :issue, :url => project_issues_path(@project), :html => { :class => :form } do |f|
= render :partial => "form", :locals => {:f => f}

View File

@ -0,0 +1,53 @@
.block
.secondary-navigation
%ul.wat-cf
%li.first= link_to t("layout.issues.list"), project_issues_path(@project)
%li= link_to t("layout.issues.edit"), edit_project_issue_path(@project, @issue) if can? :edit, @issue
.content
.inner
%p
%b
= t("activerecord.attributes.issue.title")
\:
= @issue.title
%p
%b
= t("activerecord.attributes.issue.body")
\:
= @issue.body
%p
%b
= t('layout.issues.status')
\:
= @issue.status
%a{ :name => "comments" }
.block
.secondary-navigation
%ul.wat-cf
.content
%h2.title
= t("layout.issues.comments_header")
.inner
%table.table
%tr
%th.first= t("activerecord.attributes.user.uname")
%th.first= t("activerecord.attributes.comment.body")
%th.last &nbsp;
- @issue.comments.each do |comment|
%tr{:class => cycle("odd", "even")}
%td
= comment.user.uname
%td
= comment.body
%td.last
= link_to t("layout.edit"), edit_project_issue_comment_path(@project, @issue, comment) if can? :update, comment
= link_to image_tag("web-app-theme/icons/cross.png", :alt => t("layout.delete")) + " " + t("layout.delete"), project_issue_comment_path(@project, @issue, comment), :method => "delete", :class => "button", :confirm => t("layout.comment.confirm_delete") if can? :delete, comment
.block
.content
%h2.title
= t("layout.comments.new_header")
.inner
= form_for :comment, :url => project_issue_comments_path(@project, @issue), :html => { :class => :form } do |f|
= render :partial => "comments/form", :locals => {:f => f}

View File

@ -6,6 +6,7 @@
%li.active= link_to t("layout.projects.show"), project_path(@project) %li.active= link_to t("layout.projects.show"), project_path(@project)
%li= link_to t("layout.git.repositories.source"), project_repo_path(@project) %li= link_to t("layout.git.repositories.source"), project_repo_path(@project)
%li= link_to t("layout.projects.build"), build_project_path(@project) %li= link_to t("layout.projects.build"), build_project_path(@project)
%li= link_to t("layout.projects.issues"), project_issues_path(@project)
.content .content
.inner .inner

View File

@ -68,6 +68,9 @@ Rosa::Application.routes.draw do
end end
resources :projects do resources :projects do
resources :issues do
resources :comments, :only => [:edit, :create, :update, :destroy]
end
resource :repo, :controller => "git/repositories", :only => [:show] resource :repo, :controller => "git/repositories", :only => [:show]
resources :build_lists, :only => [:index, :show] do resources :build_lists, :only => [:index, :show] do
collection do collection do

View File

@ -0,0 +1,20 @@
class CreateIssues < ActiveRecord::Migration
def self.up
create_table :issues do |t|
t.integer :serial_id
t.integer :project_id
t.integer :user_id
t.string :title
t.text :body
t.string :status
t.timestamps
end
add_index :issues, [:project_id, :serial_id], :unique => true
end
def self.down
drop_table :issues
end
end

View File

@ -0,0 +1,16 @@
class CreateComments < ActiveRecord::Migration
def self.up
create_table :comments do |t|
t.integer :commentable_id
t.string :commentable_type
t.integer :user_id
t.text :body
t.timestamps
end
end
def self.down
drop_table :comments
end
end

View File

@ -0,0 +1,9 @@
class AddHasIssuesToProjects < ActiveRecord::Migration
def self.up
add_column :projects, :has_issues, :boolean, :default => true
end
def self.down
remove_column :projects, :has_issues
end
end

View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20111130181101) do ActiveRecord::Schema.define(:version => 20111219073859) do
create_table "arches", :force => true do |t| create_table "arches", :force => true do |t|
t.string "name", :null => false t.string "name", :null => false
@ -83,6 +83,15 @@ ActiveRecord::Schema.define(:version => 20111130181101) do
t.datetime "updated_at" t.datetime "updated_at"
end end
create_table "comments", :force => true do |t|
t.integer "commentable_id"
t.string "commentable_type"
t.integer "user_id"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "containers", :force => true do |t| create_table "containers", :force => true do |t|
t.string "name", :null => false t.string "name", :null => false
t.integer "project_id", :null => false t.integer "project_id", :null => false
@ -140,6 +149,19 @@ ActiveRecord::Schema.define(:version => 20111130181101) do
t.string "uname" t.string "uname"
end end
create_table "issues", :force => true do |t|
t.integer "serial_id"
t.integer "project_id"
t.integer "user_id"
t.string "title"
t.text "body"
t.string "status"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "issues", ["project_id", "serial_id"], :name => "index_issues_on_project_id_and_serial_id", :unique => true
create_table "platforms", :force => true do |t| create_table "platforms", :force => true do |t|
t.string "description" t.string "description"
t.string "name" t.string "name"
@ -211,6 +233,7 @@ ActiveRecord::Schema.define(:version => 20111130181101) do
t.integer "category_id" t.integer "category_id"
t.text "description" t.text "description"
t.string "ancestry" t.string "ancestry"
t.boolean "has_issues", :default => true
end end
add_index "projects", ["category_id"], :name => "index_projects_on_category_id" add_index "projects", ["category_id"], :name => "index_projects_on_category_id"

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe CommentsController do
end

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe IssuesController do
end

View File

@ -0,0 +1,6 @@
# Read about factories at http://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :comment do
end
end

6
spec/factories/issues.rb Normal file
View File

@ -0,0 +1,6 @@
# Read about factories at http://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :issue do
end
end

View File

@ -0,0 +1,15 @@
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the CommentsHelper. For example:
#
# describe CommentsHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end
describe CommentsHelper do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

@ -0,0 +1,15 @@
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the IssuesHelper. For example:
#
# describe IssuesHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end
describe IssuesHelper do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe Comment do
pending "add some examples to (or delete) #{__FILE__}"
end

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe Issue do
pending "add some examples to (or delete) #{__FILE__}"
end