snippets are ready
This commit is contained in:
parent
526ad466c8
commit
9265de3d25
28 changed files with 366 additions and 8 deletions
9
app/assets/javascripts/snippets.js
Normal file
9
app/assets/javascripts/snippets.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
$(document).ready(function(){
|
||||
$("#snippets-table .snippet").live('click', function(e){
|
||||
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
|
||||
location.href = $(this).attr("url");
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -22,8 +22,8 @@ td.linenos{
|
|||
|
||||
.highlight{
|
||||
background:none;
|
||||
padding:10px 0px 0px 0;
|
||||
margin-left:10px;
|
||||
padding:10px 0px 0px 10px;
|
||||
margin-left:0px;
|
||||
}
|
||||
.highlight pre{
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ td.linenos {
|
|||
}
|
||||
|
||||
td.code .highlight {
|
||||
overflow-x: scroll;
|
||||
overflow: auto;
|
||||
}
|
||||
table.highlighttable pre{
|
||||
padding:0;
|
||||
|
|
|
@ -310,6 +310,7 @@ input.ssh_project_url {
|
|||
}
|
||||
|
||||
#projects-list .project,
|
||||
#snippets-table .snippet,
|
||||
#issues-table .issue{
|
||||
cursor:pointer;
|
||||
|
||||
|
@ -360,6 +361,8 @@ input.ssh_project_url {
|
|||
.user_new,
|
||||
.edit_user,
|
||||
.new_project,
|
||||
.new_snippet,
|
||||
.edit_snippet,
|
||||
.edit_project {
|
||||
input[type='text'],
|
||||
input[type='email'],
|
||||
|
|
3
app/assets/stylesheets/snippets.css.scss
Normal file
3
app/assets/stylesheets/snippets.css.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
// Place all the styles related to the Snippets controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
@ -41,6 +41,8 @@ class NotesController < ApplicationController
|
|||
Notify.note_commit_email(u, @note).deliver
|
||||
when "Issue" then
|
||||
Notify.note_issue_email(u, @note).deliver
|
||||
when "Snippet"
|
||||
true
|
||||
else
|
||||
Notify.note_wall_email(u, @note).deliver
|
||||
end
|
||||
|
|
63
app/controllers/snippets_controller.rb
Normal file
63
app/controllers/snippets_controller.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
class SnippetsController < ApplicationController
|
||||
before_filter :authenticate_user!
|
||||
before_filter :project
|
||||
|
||||
# Authorize
|
||||
before_filter :add_project_abilities
|
||||
before_filter :authorize_read_snippet!
|
||||
before_filter :authorize_write_snippet!, :only => [:new, :create, :close, :edit, :update, :sort]
|
||||
|
||||
respond_to :html
|
||||
|
||||
def index
|
||||
@snippets = @project.snippets
|
||||
end
|
||||
|
||||
def new
|
||||
@snippet = @project.snippets.new
|
||||
end
|
||||
|
||||
def create
|
||||
@snippet = @project.snippets.new(params[:snippet])
|
||||
@snippet.author = current_user
|
||||
@snippet.save
|
||||
|
||||
if @snippet.valid?
|
||||
redirect_to [@project, @snippet]
|
||||
else
|
||||
respond_with(@snippet)
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@snippet = @project.snippets.find(params[:id])
|
||||
end
|
||||
|
||||
def update
|
||||
@snippet = @project.snippets.find(params[:id])
|
||||
@snippet.update_attributes(params[:snippet])
|
||||
|
||||
if @snippet.valid?
|
||||
redirect_to [@project, @snippet]
|
||||
else
|
||||
respond_with(@snippet)
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
@snippet = @project.snippets.find(params[:id])
|
||||
@notes = @snippet.notes
|
||||
@note = @project.notes.new(:noteable => @snippet)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@snippet = @project.snippets.find(params[:id])
|
||||
authorize_admin_snippet! unless @snippet.author == current_user
|
||||
|
||||
@snippet.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.js { render :nothing => true }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -53,7 +53,7 @@ module ApplicationHelper
|
|||
[projects, default_nav, project_nav].flatten.to_json
|
||||
end
|
||||
|
||||
def handle_file_type(file_name, mime_type)
|
||||
def handle_file_type(file_name, mime_type = nil)
|
||||
if file_name =~ /(\.rb|\.ru|\.rake|Rakefile|\.gemspec|\.rbx|Gemfile)$/
|
||||
:ruby
|
||||
elsif file_name =~ /\.py$/
|
||||
|
|
2
app/helpers/snippets_helper.rb
Normal file
2
app/helpers/snippets_helper.rb
Normal file
|
@ -0,0 +1,2 @@
|
|||
module SnippetsHelper
|
||||
end
|
|
@ -12,6 +12,7 @@ class Ability
|
|||
rules << [
|
||||
:read_project,
|
||||
:read_issue,
|
||||
:read_snippet,
|
||||
:read_team_member,
|
||||
:read_note
|
||||
] if project.readers.include?(user)
|
||||
|
@ -19,12 +20,14 @@ class Ability
|
|||
rules << [
|
||||
:write_project,
|
||||
:write_issue,
|
||||
:write_snippet,
|
||||
:write_note
|
||||
] if project.writers.include?(user)
|
||||
|
||||
rules << [
|
||||
:admin_project,
|
||||
:admin_issue,
|
||||
:admin_snippet,
|
||||
:admin_team_member,
|
||||
:admin_note
|
||||
] if project.admins.include?(user)
|
||||
|
|
|
@ -7,6 +7,7 @@ class Project < ActiveRecord::Base
|
|||
has_many :users_projects, :dependent => :destroy
|
||||
has_many :users, :through => :users_projects
|
||||
has_many :notes, :dependent => :destroy
|
||||
has_many :snippets, :dependent => :destroy
|
||||
|
||||
validates :name,
|
||||
:uniqueness => true,
|
||||
|
|
31
app/models/snippet.rb
Normal file
31
app/models/snippet.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
class Snippet < ActiveRecord::Base
|
||||
belongs_to :project
|
||||
belongs_to :author, :class_name => "User"
|
||||
has_many :notes, :as => :noteable
|
||||
|
||||
attr_protected :author, :author_id, :project, :project_id
|
||||
|
||||
validates_presence_of :project_id
|
||||
validates_presence_of :author_id
|
||||
|
||||
validates :title,
|
||||
:presence => true,
|
||||
:length => { :within => 0..255 }
|
||||
|
||||
validates :file_name,
|
||||
:presence => true,
|
||||
:length => { :within => 0..255 }
|
||||
|
||||
validates :content,
|
||||
:presence => true,
|
||||
:length => { :within => 0..10000 }
|
||||
|
||||
|
||||
def self.content_types
|
||||
[
|
||||
".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java",
|
||||
".haml", ".html", ".sass", ".scss", ".xml", ".php", ".erb",
|
||||
".js", ".sh", ".coffee", ".yml", ".md"
|
||||
]
|
||||
end
|
||||
end
|
|
@ -1,8 +1,6 @@
|
|||
= form_for(@project, :remote => true) do |f|
|
||||
%div.form_content
|
||||
- if @project.new_record?
|
||||
%h1 New Project
|
||||
- else
|
||||
- unless @project.new_record?
|
||||
%h1 Edit Project
|
||||
- if @project.errors.any?
|
||||
#error_explanation
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
Wall
|
||||
- if @project.common_notes.count > 0
|
||||
%span{ :class => "top_menu_count" }= @project.common_notes.count
|
||||
%span
|
||||
= link_to project_snippets_path(@project), :class => (controller.controller_name == "snippets") ? "current" : nil do
|
||||
Snippets
|
||||
- if @project.snippets.count > 0
|
||||
%span{ :class => "top_menu_count" }= @project.snippets.count
|
||||
|
||||
- if @commit
|
||||
%span= link_to truncate(commit_name(@project,@commit), :length => 15), project_commit_path(@project, :id => @commit.id), :class => current_page?(:controller => "commits", :action => "show", :project_id => @project, :id => @commit.id) ? "current" : nil
|
||||
|
|
22
app/views/snippets/_form.html.haml
Normal file
22
app/views/snippets/_form.html.haml
Normal file
|
@ -0,0 +1,22 @@
|
|||
%div
|
||||
= form_for [@project, @snippet] do |f|
|
||||
-if @snippet.errors.any?
|
||||
%ul
|
||||
- @snippet.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
%table.round-borders
|
||||
%tr
|
||||
%td= f.label :title
|
||||
%td= f.text_field :title, :placeholder => "Example Snippet"
|
||||
%tr
|
||||
%td= f.label :file_name
|
||||
%td= f.text_field :file_name, :placeholder => "example.rb"
|
||||
%tr
|
||||
%td{:colspan => 2}
|
||||
= f.label :content, "Code"
|
||||
%br
|
||||
= f.text_area :content, :style => "height:240px;width:932px;"
|
||||
|
||||
.actions.prepend-top
|
||||
= f.submit 'Save', :class => "lbutton vm"
|
11
app/views/snippets/_snippet.html.haml
Normal file
11
app/views/snippets/_snippet.html.haml
Normal file
|
@ -0,0 +1,11 @@
|
|||
%tr{ :id => dom_id(snippet), :class => "snippet", :url => project_snippet_path(@project, snippet) }
|
||||
%td
|
||||
= image_tag gravatar_icon(snippet.author.email), :class => "left", :width => 40, :style => "padding:0 5px;"
|
||||
= truncate snippet.author.name, :lenght => 20
|
||||
%td= html_escape snippet.title
|
||||
%td= html_escape snippet.file_name
|
||||
%td
|
||||
- if can?(current_user, :admin_snippet, @project) || snippet.author == current_user
|
||||
= link_to 'Edit', edit_project_snippet_path(@project, snippet), :class => "lbutton positive"
|
||||
- if can?(current_user, :admin_snippet, @project) || snippet.author == current_user
|
||||
= link_to 'Destroy', [@project, snippet], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "lbutton delete-snippet negative", :id => "destroy_snippet_#{snippet.id}"
|
1
app/views/snippets/edit.html.haml
Normal file
1
app/views/snippets/edit.html.haml
Normal file
|
@ -0,0 +1 @@
|
|||
= render "snippets/form"
|
14
app/views/snippets/index.html.haml
Normal file
14
app/views/snippets/index.html.haml
Normal file
|
@ -0,0 +1,14 @@
|
|||
%div
|
||||
- if can? current_user, :write_snippet, @project
|
||||
.left= link_to 'New Snippet', new_project_snippet_path(@project), :class => "lbutton vm"
|
||||
|
||||
%table.round-borders#snippets-table
|
||||
%tr
|
||||
%th Author
|
||||
%th Title
|
||||
%th File name
|
||||
%th
|
||||
= render @snippets
|
||||
:javascript
|
||||
$('.delete-snippet').live('ajax:success', function() {
|
||||
$(this).closest('tr').fadeOut(); });
|
1
app/views/snippets/new.html.haml
Normal file
1
app/views/snippets/new.html.haml
Normal file
|
@ -0,0 +1 @@
|
|||
= render "snippets/form"
|
22
app/views/snippets/show.html.haml
Normal file
22
app/views/snippets/show.html.haml
Normal file
|
@ -0,0 +1,22 @@
|
|||
%h2
|
||||
= "Snippet ##{@snippet.id} - #{@snippet.title}"
|
||||
|
||||
.view_file
|
||||
.view_file_header
|
||||
%strong
|
||||
= @snippet.file_name
|
||||
%br/
|
||||
.view_file_content
|
||||
- ft = handle_file_type(@snippet.file_name)
|
||||
:erb
|
||||
<%= raw Albino.colorize(@snippet.content, ft, :html, 'utf-8', "linenos=True") %>
|
||||
|
||||
- if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user
|
||||
= link_to 'Edit', edit_project_snippet_path(@project, @snippet), :class => "lbutton positive"
|
||||
- if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user
|
||||
= link_to 'Destroy', [@project, @snippet], :confirm => 'Are you sure?', :method => :delete, :class => "lbutton delete-snippet negative", :id => "destroy_snippet_#{@snippet.id}"
|
||||
%br
|
||||
.snippet_notes= render "notes/notes"
|
||||
|
||||
.clear
|
||||
|
|
@ -38,6 +38,8 @@ Gitlab::Application.routes.draw do
|
|||
}
|
||||
|
||||
end
|
||||
|
||||
resources :snippets
|
||||
resources :commits
|
||||
resources :team_members
|
||||
resources :issues do
|
||||
|
|
12
db/migrate/20111016183422_create_snippets.rb
Normal file
12
db/migrate/20111016183422_create_snippets.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class CreateSnippets < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :snippets do |t|
|
||||
t.string :title
|
||||
t.text :content
|
||||
t.integer :author_id, :null => false
|
||||
t.integer :project_id, :null => false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddContentTypeToSnippets < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :snippets, :content_type, :string, :null => false, :default => "txt"
|
||||
end
|
||||
end
|
6
db/migrate/20111016195506_add_file_name_to_snippets.rb
Normal file
6
db/migrate/20111016195506_add_file_name_to_snippets.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
class AddFileNameToSnippets < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :snippets, :file_name, :string
|
||||
remove_column :snippets, :content_type
|
||||
end
|
||||
end
|
12
db/schema.rb
12
db/schema.rb
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20111015154310) do
|
||||
ActiveRecord::Schema.define(:version => 20111016195506) do
|
||||
|
||||
create_table "issues", :force => true do |t|
|
||||
t.string "title"
|
||||
|
@ -56,6 +56,16 @@ ActiveRecord::Schema.define(:version => 20111015154310) do
|
|||
t.integer "owner_id"
|
||||
end
|
||||
|
||||
create_table "snippets", :force => true do |t|
|
||||
t.string "title"
|
||||
t.text "content"
|
||||
t.integer "author_id", :null => false
|
||||
t.integer "project_id", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "file_name"
|
||||
end
|
||||
|
||||
create_table "users", :force => true do |t|
|
||||
t.string "email", :default => "", :null => false
|
||||
t.string "encrypted_password", :limit => 128, :default => "", :null => false
|
||||
|
|
|
@ -35,6 +35,12 @@ Factory.add(:issue, Issue) do |obj|
|
|||
obj.content = Faker::Lorem.sentences
|
||||
end
|
||||
|
||||
Factory.add(:snippet, Snippet) do |obj|
|
||||
obj.title = Faker::Lorem.sentence
|
||||
obj.file_name = Faker::Lorem.sentence
|
||||
obj.content = Faker::Lorem.sentences
|
||||
end
|
||||
|
||||
Factory.add(:note, Note) do |obj|
|
||||
obj.note = Faker::Lorem.sentence
|
||||
end
|
||||
|
|
16
spec/models/snippet_spec.rb
Normal file
16
spec/models/snippet_spec.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Snippet do
|
||||
describe "Associations" do
|
||||
it { should belong_to(:project) }
|
||||
it { should belong_to(:author) }
|
||||
end
|
||||
|
||||
describe "Validation" do
|
||||
it { should validate_presence_of(:title) }
|
||||
it { should validate_presence_of(:author_id) }
|
||||
it { should validate_presence_of(:project_id) }
|
||||
it { should validate_presence_of(:file_name) }
|
||||
it { should validate_presence_of(:content) }
|
||||
end
|
||||
end
|
|
@ -107,5 +107,14 @@ describe "Projects" do
|
|||
it { project_issues_path(@project).should be_denied_for :user }
|
||||
it { project_issues_path(@project).should be_denied_for :visitor }
|
||||
end
|
||||
|
||||
describe "GET /project_code/snippets" do
|
||||
it { project_snippets_path(@project).should be_allowed_for @u1 }
|
||||
it { project_snippets_path(@project).should be_allowed_for @u3 }
|
||||
it { project_snippets_path(@project).should be_denied_for :admin }
|
||||
it { project_snippets_path(@project).should be_denied_for @u2 }
|
||||
it { project_snippets_path(@project).should be_denied_for :user }
|
||||
it { project_snippets_path(@project).should be_denied_for :visitor }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
101
spec/requests/snippets_spec.rb
Normal file
101
spec/requests/snippets_spec.rb
Normal file
|
@ -0,0 +1,101 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe "Snippets" do
|
||||
let(:project) { Factory :project }
|
||||
|
||||
before do
|
||||
login_as :user
|
||||
project.add_access(@user, :read, :write)
|
||||
end
|
||||
|
||||
describe "GET /snippets" do
|
||||
before do
|
||||
@snippet = Factory :snippet,
|
||||
:author => @user,
|
||||
:project => project
|
||||
|
||||
visit project_snippets_path(project)
|
||||
end
|
||||
|
||||
subject { page }
|
||||
|
||||
it { should have_content(@snippet.title) }
|
||||
it { should have_content(@snippet.project.name) }
|
||||
it { should have_content(@snippet.author.name) }
|
||||
|
||||
describe "Destroy" do
|
||||
before do
|
||||
# admin access to remove snippet
|
||||
@user.users_projects.destroy_all
|
||||
project.add_access(@user, :read, :write, :admin)
|
||||
visit project_snippets_path(project)
|
||||
end
|
||||
|
||||
it "should remove entry" do
|
||||
expect {
|
||||
click_link "destroy_snippet_#{@snippet.id}"
|
||||
}.to change { Snippet.count }.by(-1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "New snippet" do
|
||||
before do
|
||||
visit project_snippets_path(project)
|
||||
click_link "New Snippet"
|
||||
end
|
||||
|
||||
it "should open new snippet popup" do
|
||||
page.current_path.should == new_project_snippet_path(project)
|
||||
end
|
||||
|
||||
describe "fill in" do
|
||||
before do
|
||||
fill_in "snippet_title", :with => "login function"
|
||||
fill_in "snippet_file_name", :with => "test.rb"
|
||||
fill_in "snippet_content", :with => "def login; end"
|
||||
end
|
||||
|
||||
it { expect { click_button "Save" }.to change {Snippet.count}.by(1) }
|
||||
|
||||
it "should add new snippet to table" do
|
||||
click_button "Save"
|
||||
page.current_path.should == project_snippet_path(project, Snippet.last)
|
||||
page.should have_content "login function"
|
||||
page.should have_content "test.rb"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Edit snippet" do
|
||||
before do
|
||||
@snippet = Factory :snippet,
|
||||
:author => @user,
|
||||
:project => project
|
||||
visit project_snippets_path(project)
|
||||
click_link "Edit"
|
||||
end
|
||||
|
||||
it "should open edit page" do
|
||||
page.current_path.should == edit_project_snippet_path(project, @snippet)
|
||||
end
|
||||
|
||||
describe "fill in" do
|
||||
before do
|
||||
fill_in "snippet_title", :with => "login function"
|
||||
fill_in "snippet_file_name", :with => "test.rb"
|
||||
fill_in "snippet_content", :with => "def login; end"
|
||||
end
|
||||
|
||||
it { expect { click_button "Save" }.to_not change {Snippet.count} }
|
||||
|
||||
it "should update snippet fields" do
|
||||
click_button "Save"
|
||||
|
||||
page.current_path.should == project_snippet_path(project, @snippet)
|
||||
page.should have_content "login function"
|
||||
page.should have_content "test.rb"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue