| |   |
| 1 | 1 | class Project < ActiveRecord::Base |
| 2 | 2 | acts_as_taggable |
| 3 | | |
| 3 | |
| 4 | 4 | belongs_to :user |
| 5 | 5 | has_many :comments, :dependent => :destroy |
| 6 | 6 | has_many :repositories, :order => "repositories.mainline desc, repositories.created_at asc", |
| 7 | 7 | :dependent => :destroy |
| 8 | | has_one :mainline_repository, :conditions => ["mainline = ?", true], |
| 8 | has_one :mainline_repository, :conditions => ["mainline = ?", true], |
| 9 | 9 | :class_name => "Repository" |
| 10 | 10 | has_many :repository_clones, :conditions => ["mainline = ?", false], |
| 11 | 11 | :class_name => "Repository" |
| 12 | | |
| 13 | | is_indexed :fields => ["title", "description", "slug"], |
| 12 | |
| 13 | is_indexed :fields => ["title", "description", "slug"], |
| 14 | 14 | :concatenate => [ |
| 15 | | { :class_name => 'Tag', |
| 16 | | :field => 'name', |
| 15 | { :class_name => 'Tag', |
| 16 | :field => 'name', |
| 17 | 17 | :as => 'category', |
| 18 | | :association_sql => "LEFT OUTER JOIN taggings ON taggings.taggable_id = projects.id " + |
| 18 | :association_sql => "LEFT OUTER JOIN taggings ON taggings.taggable_id = projects.id " + |
| 19 | 19 | "AND taggings.taggable_type = 'Project' LEFT OUTER JOIN tags ON taggings.tag_id = tags.id" |
| 20 | 20 | }], |
| 21 | 21 | :include => [{ |
| … | … | |
| 23 | 23 | :field => "login", |
| 24 | 24 | :as => "user" |
| 25 | 25 | }] |
| 26 | | |
| 27 | | |
| 26 | |
| 27 | |
| 28 | 28 | URL_FORMAT_RE = /^(http|https|nntp):\/\//.freeze |
| 29 | 29 | validates_presence_of :title, :user_id, :slug |
| 30 | 30 | validates_uniqueness_of :slug, :case_sensitive => false |
| 31 | | validates_format_of :slug, :with => /^[a-z0-9_\-]+$/i, |
| 31 | validates_format_of :slug, :with => /^[a-z0-9_\-]+$/i, |
| 32 | 32 | :message => "must match something in the range of [a-z0-9_\-]+" |
| 33 | | validates_format_of :home_url, :with => URL_FORMAT_RE, |
| 33 | validates_format_of :home_url, :with => URL_FORMAT_RE, |
| 34 | 34 | :if => proc{|record| !record.home_url.blank? }, |
| 35 | 35 | :message => "Must begin with http(s)" |
| 36 | 36 | validates_format_of :mailinglist_url, :with => URL_FORMAT_RE, |
| 37 | 37 | :if => proc{|record| !record.mailinglist_url.blank? }, |
| 38 | 38 | :message => "Must begin with http(s)" |
| 39 | | validates_format_of :bugtracker_url, :with => URL_FORMAT_RE, |
| 39 | validates_format_of :bugtracker_url, :with => URL_FORMAT_RE, |
| 40 | 40 | :if => proc{|record| !record.bugtracker_url.blank? }, |
| 41 | 41 | :message => "Must begin with http(s)" |
| 42 | | |
| 42 | |
| 43 | 43 | before_validation :downcase_slug |
| 44 | 44 | after_create :create_mainline_repository |
| 45 | | |
| 45 | |
| 46 | 46 | LICENSES = [ |
| 47 | 47 | 'Academic Free License v3.0', |
| 48 | 48 | 'MIT License', |
| … | … | |
| 68 | 68 | 'Other/Proprietary License', |
| 69 | 69 | 'None', |
| 70 | 70 | ] |
| 71 | | |
| 71 | |
| 72 | 72 | def self.find_by_slug!(slug, opts = {}) |
| 73 | 73 | find_by_slug(slug, opts) || raise(ActiveRecord::RecordNotFound) |
| 74 | 74 | end |
| 75 | | |
| 75 | |
| 76 | 76 | def self.per_page() 20 end |
| 77 | | |
| 77 | |
| 78 | 78 | def self.top_tags(limit = 10) |
| 79 | 79 | tag_counts(:limit => limit, :order => "count desc") |
| 80 | 80 | end |
| 81 | | |
| 81 | |
| 82 | 82 | def to_param |
| 83 | 83 | slug |
| 84 | 84 | end |
| 85 | | |
| 85 | |
| 86 | 86 | def admin?(candidate) |
| 87 | 87 | candidate == user |
| 88 | 88 | end |
| 89 | | |
| 89 | |
| 90 | 90 | def can_be_deleted_by?(candidate) |
| 91 | 91 | (candidate == user) && (repositories.size == 1) |
| 92 | 92 | end |
| 93 | | |
| 93 | |
| 94 | 94 | def tag_list=(tag_list) |
| 95 | 95 | tag_list.gsub!(",", "") |
| 96 | | |
| 96 | |
| 97 | 97 | super |
| 98 | 98 | end |
| 99 | | |
| 99 | |
| 100 | def home_url=(url) |
| 101 | self[:home_url] = clean_url(url) |
| 102 | end |
| 103 | |
| 104 | def mailinglist_url=(url) |
| 105 | self[:mailinglist_url] = clean_url(url) |
| 106 | end |
| 107 | |
| 108 | def bugtracker_url=(url) |
| 109 | self[:bugtracker_url] = clean_url(url) |
| 110 | end |
| 111 | |
| 100 | 112 | def stripped_description |
| 101 | 113 | description.gsub(/<\/?[^>]*>/, "") |
| 102 | 114 | # sanitizer = HTML::WhiteListSanitizer.new |
| 103 | 115 | # sanitizer.sanitize(description, :tags => %w(str), :attributes => %w(class)) |
| 104 | 116 | end |
| 105 | | |
| 117 | |
| 106 | 118 | def to_xml(opts = {}) |
| 107 | 119 | info = Proc.new { |options| |
| 108 | 120 | builder = options[:builder] |
| 109 | 121 | builder.owner user.login |
| 110 | | |
| 111 | | builder.repositories :type => "array" do |
| 122 | |
| 123 | builder.repositories :type => "array" do |
| 112 | 124 | repositories.each { |repo| |
| 113 | 125 | builder.repository do |
| 114 | 126 | builder.id repo.id |
| … | … | |
| 132 | 132 | } |
| 133 | 133 | super({:procs => [info]}.merge(opts)) |
| 134 | 134 | end |
| 135 | | |
| 135 | |
| 136 | 136 | protected |
| 137 | 137 | def create_mainline_repository |
| 138 | 138 | self.repositories.create!(:user => self.user, :name => "mainline") |
| 139 | 139 | end |
| 140 | | |
| 140 | |
| 141 | 141 | def downcase_slug |
| 142 | 142 | slug.downcase! if slug |
| 143 | 143 | end |
| 144 | | |
| 144 | |
| 145 | # Try our best to guess the url |
| 146 | def clean_url(url) |
| 147 | begin |
| 148 | url = "http://#{url}" if URI.parse(url).class == URI::Generic |
| 149 | rescue |
| 150 | end |
| 151 | url |
| 152 | end |
| 145 | 153 | end |
| toggle raw diff |
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1,21 +1,21 @@
class Project < ActiveRecord::Base
acts_as_taggable
-
+
belongs_to :user
has_many :comments, :dependent => :destroy
has_many :repositories, :order => "repositories.mainline desc, repositories.created_at asc",
:dependent => :destroy
- has_one :mainline_repository, :conditions => ["mainline = ?", true],
+ has_one :mainline_repository, :conditions => ["mainline = ?", true],
:class_name => "Repository"
has_many :repository_clones, :conditions => ["mainline = ?", false],
:class_name => "Repository"
-
- is_indexed :fields => ["title", "description", "slug"],
+
+ is_indexed :fields => ["title", "description", "slug"],
:concatenate => [
- { :class_name => 'Tag',
- :field => 'name',
+ { :class_name => 'Tag',
+ :field => 'name',
:as => 'category',
- :association_sql => "LEFT OUTER JOIN taggings ON taggings.taggable_id = projects.id " +
+ :association_sql => "LEFT OUTER JOIN taggings ON taggings.taggable_id = projects.id " +
"AND taggings.taggable_type = 'Project' LEFT OUTER JOIN tags ON taggings.tag_id = tags.id"
}],
:include => [{
@@ -23,26 +23,26 @@ class Project < ActiveRecord::Base
:field => "login",
:as => "user"
}]
-
-
+
+
URL_FORMAT_RE = /^(http|https|nntp):\/\//.freeze
validates_presence_of :title, :user_id, :slug
validates_uniqueness_of :slug, :case_sensitive => false
- validates_format_of :slug, :with => /^[a-z0-9_\-]+$/i,
+ validates_format_of :slug, :with => /^[a-z0-9_\-]+$/i,
:message => "must match something in the range of [a-z0-9_\-]+"
- validates_format_of :home_url, :with => URL_FORMAT_RE,
+ validates_format_of :home_url, :with => URL_FORMAT_RE,
:if => proc{|record| !record.home_url.blank? },
:message => "Must begin with http(s)"
validates_format_of :mailinglist_url, :with => URL_FORMAT_RE,
:if => proc{|record| !record.mailinglist_url.blank? },
:message => "Must begin with http(s)"
- validates_format_of :bugtracker_url, :with => URL_FORMAT_RE,
+ validates_format_of :bugtracker_url, :with => URL_FORMAT_RE,
:if => proc{|record| !record.bugtracker_url.blank? },
:message => "Must begin with http(s)"
-
+
before_validation :downcase_slug
after_create :create_mainline_repository
-
+
LICENSES = [
'Academic Free License v3.0',
'MIT License',
@@ -68,47 +68,59 @@ class Project < ActiveRecord::Base
'Other/Proprietary License',
'None',
]
-
+
def self.find_by_slug!(slug, opts = {})
find_by_slug(slug, opts) || raise(ActiveRecord::RecordNotFound)
end
-
+
def self.per_page() 20 end
-
+
def self.top_tags(limit = 10)
tag_counts(:limit => limit, :order => "count desc")
end
-
+
def to_param
slug
end
-
+
def admin?(candidate)
candidate == user
end
-
+
def can_be_deleted_by?(candidate)
(candidate == user) && (repositories.size == 1)
end
-
+
def tag_list=(tag_list)
tag_list.gsub!(",", "")
-
+
super
end
-
+
+ def home_url=(url)
+ self[:home_url] = clean_url(url)
+ end
+
+ def mailinglist_url=(url)
+ self[:mailinglist_url] = clean_url(url)
+ end
+
+ def bugtracker_url=(url)
+ self[:bugtracker_url] = clean_url(url)
+ end
+
def stripped_description
description.gsub(/<\/?[^>]*>/, "")
# sanitizer = HTML::WhiteListSanitizer.new
# sanitizer.sanitize(description, :tags => %w(str), :attributes => %w(class))
end
-
+
def to_xml(opts = {})
info = Proc.new { |options|
builder = options[:builder]
builder.owner user.login
-
- builder.repositories :type => "array" do
+
+ builder.repositories :type => "array" do
repositories.each { |repo|
builder.repository do
builder.id repo.id
@@ -120,14 +132,22 @@ class Project < ActiveRecord::Base
}
super({:procs => [info]}.merge(opts))
end
-
+
protected
def create_mainline_repository
self.repositories.create!(:user => self.user, :name => "mainline")
end
-
+
def downcase_slug
slug.downcase! if slug
end
-
+
+ # Try our best to guess the url
+ def clean_url(url)
+ begin
+ url = "http://#{url}" if URI.parse(url).class == URI::Generic
+ rescue
+ end
+ url
+ end
end |
| |   |
| 1 | 1 | require File.dirname(__FILE__) + '/../spec_helper' |
| 2 | 2 | |
| 3 | 3 | describe Project do |
| 4 | | |
| 4 | |
| 5 | 5 | def create_project(options={}) |
| 6 | 6 | Project.new({ |
| 7 | | :title => "foo project", |
| 8 | | :slug => "foo", |
| 7 | :title => "foo project", |
| 8 | :slug => "foo", |
| 9 | 9 | :user => users(:johan) |
| 10 | 10 | }.merge(options)) |
| 11 | 11 | end |
| 12 | | |
| 12 | |
| 13 | 13 | it "should have valid associations" do |
| 14 | 14 | create_project.should have_valid_associations |
| 15 | 15 | end |
| 16 | | |
| 16 | |
| 17 | 17 | it "should have a title to be valid" do |
| 18 | 18 | project = create_project(:title => nil) |
| 19 | 19 | project.should_not be_valid |
| 20 | 20 | project.title = "foo" |
| 21 | 21 | project.should be_valid |
| 22 | 22 | end |
| 23 | | |
| 23 | |
| 24 | 24 | it "should have a slug to be valid" do |
| 25 | 25 | project = create_project(:slug => nil) |
| 26 | 26 | project.should_not be_valid |
| 27 | 27 | end |
| 28 | | |
| 28 | |
| 29 | 29 | it "should have a unique slug to be valid" do |
| 30 | 30 | p1 = create_project |
| 31 | 31 | p1.save! |
| … | … | |
| 33 | 33 | p2.should_not be_valid |
| 34 | 34 | p2.should have(1).error_on(:slug) |
| 35 | 35 | end |
| 36 | | |
| 36 | |
| 37 | 37 | it "should have an alphanumeric slug" do |
| 38 | 38 | project = create_project(:slug => "asd asd") |
| 39 | 39 | project.valid? |
| 40 | 40 | project.should_not be_valid |
| 41 | 41 | end |
| 42 | | |
| 42 | |
| 43 | 43 | it "should downcase the slug before validation" do |
| 44 | 44 | project = create_project(:slug => "FOO") |
| 45 | 45 | project.valid? |
| 46 | 46 | project.slug.should == "foo" |
| 47 | 47 | end |
| 48 | | |
| 48 | |
| 49 | 49 | it "creates an initial repository for itself" do |
| 50 | 50 | project = create_project |
| 51 | 51 | project.save |
| … | … | |
| 54 | 54 | project.repositories.first.user.should == project.user |
| 55 | 55 | project.user.can_write_to?(project.repositories.first).should == true |
| 56 | 56 | end |
| 57 | | |
| 57 | |
| 58 | 58 | it "finds a project by slug or raises" do |
| 59 | 59 | Project.find_by_slug!(projects(:johans).slug).should == projects(:johans) |
| 60 | 60 | proc{ |
| 61 | 61 | Project.find_by_slug!("asdasdasd") |
| 62 | 62 | }.should raise_error(ActiveRecord::RecordNotFound) |
| 63 | | end |
| 64 | | |
| 63 | end |
| 64 | |
| 65 | 65 | it "has the slug as its params" do |
| 66 | 66 | projects(:johans).to_param.should == projects(:johans).slug |
| 67 | 67 | end |
| 68 | | |
| 68 | |
| 69 | 69 | it "knows if a user is a admin on a project" do |
| 70 | 70 | projects(:johans).admin?(users(:johan)).should == true |
| 71 | 71 | projects(:johans).admin?(users(:moe)).should == false |
| 72 | 72 | projects(:johans).admin?(:false).should == false |
| 73 | 73 | end |
| 74 | | |
| 74 | |
| 75 | 75 | it "knows if a user can delete the project" do |
| 76 | 76 | project = projects(:johans) |
| 77 | 77 | project.can_be_deleted_by?(users(:moe)).should == false |
| … | … | |
| 79 | 79 | project.repositories.last.destroy |
| 80 | 80 | project.reload.can_be_deleted_by?(users(:johan)).should == true |
| 81 | 81 | end |
| 82 | | |
| 82 | |
| 83 | 83 | it "should strip html tags" do |
| 84 | 84 | project = create_project(:description => "<h1>Project A</h1>\n<b>Project A</b> is a....") |
| 85 | 85 | project.stripped_description.should == "Project A\nProject A is a...." |
| 86 | 86 | end |
| 87 | | |
| 87 | |
| 88 | 88 | # it "should strip html tags, except highlights" do |
| 89 | 89 | # project = create_project(:description => %Q{<h1>Project A</h1>\n<strong class="highlight">Project A</strong> is a....}) |
| 90 | 90 | # project.stripped_description.should == %Q(Project A\n<strong class="highlight">Project A</strong> is a....) |
| 91 | 91 | # end |
| 92 | | |
| 92 | |
| 93 | it "should have valid urls ( prepending http:// if needed )" do |
| 94 | project = projects(:johans) |
| 95 | [ :home_url, :mailinglist_url, :bugtracker_url ].each do |attr| |
| 96 | project.should be_valid |
| 97 | project.send("#{attr}=", 'http://blah.com') |
| 98 | project.should be_valid |
| 99 | project.send("#{attr}=", 'ftp://blah.com') |
| 100 | project.should_not be_valid |
| 101 | project.send("#{attr}=", 'blah.com') |
| 102 | project.should be_valid |
| 103 | project.send(attr).should == 'http://blah.com' |
| 104 | end |
| 105 | end |
| 106 | |
| 93 | 107 | end |
| toggle raw diff |
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1,31 +1,31 @@
require File.dirname(__FILE__) + '/../spec_helper'
describe Project do
-
+
def create_project(options={})
Project.new({
- :title => "foo project",
- :slug => "foo",
+ :title => "foo project",
+ :slug => "foo",
:user => users(:johan)
}.merge(options))
end
-
+
it "should have valid associations" do
create_project.should have_valid_associations
end
-
+
it "should have a title to be valid" do
project = create_project(:title => nil)
project.should_not be_valid
project.title = "foo"
project.should be_valid
end
-
+
it "should have a slug to be valid" do
project = create_project(:slug => nil)
project.should_not be_valid
end
-
+
it "should have a unique slug to be valid" do
p1 = create_project
p1.save!
@@ -33,19 +33,19 @@ describe Project do
p2.should_not be_valid
p2.should have(1).error_on(:slug)
end
-
+
it "should have an alphanumeric slug" do
project = create_project(:slug => "asd asd")
project.valid?
project.should_not be_valid
end
-
+
it "should downcase the slug before validation" do
project = create_project(:slug => "FOO")
project.valid?
project.slug.should == "foo"
end
-
+
it "creates an initial repository for itself" do
project = create_project
project.save
@@ -54,24 +54,24 @@ describe Project do
project.repositories.first.user.should == project.user
project.user.can_write_to?(project.repositories.first).should == true
end
-
+
it "finds a project by slug or raises" do
Project.find_by_slug!(projects(:johans).slug).should == projects(:johans)
proc{
Project.find_by_slug!("asdasdasd")
}.should raise_error(ActiveRecord::RecordNotFound)
- end
-
+ end
+
it "has the slug as its params" do
projects(:johans).to_param.should == projects(:johans).slug
end
-
+
it "knows if a user is a admin on a project" do
projects(:johans).admin?(users(:johan)).should == true
projects(:johans).admin?(users(:moe)).should == false
projects(:johans).admin?(:false).should == false
end
-
+
it "knows if a user can delete the project" do
project = projects(:johans)
project.can_be_deleted_by?(users(:moe)).should == false
@@ -79,15 +79,29 @@ describe Project do
project.repositories.last.destroy
project.reload.can_be_deleted_by?(users(:johan)).should == true
end
-
+
it "should strip html tags" do
project = create_project(:description => "<h1>Project A</h1>\n<b>Project A</b> is a....")
project.stripped_description.should == "Project A\nProject A is a...."
end
-
+
# it "should strip html tags, except highlights" do
# project = create_project(:description => %Q{<h1>Project A</h1>\n<strong class="highlight">Project A</strong> is a....})
# project.stripped_description.should == %Q(Project A\n<strong class="highlight">Project A</strong> is a....)
# end
-
+
+ it "should have valid urls ( prepending http:// if needed )" do
+ project = projects(:johans)
+ [ :home_url, :mailinglist_url, :bugtracker_url ].each do |attr|
+ project.should be_valid
+ project.send("#{attr}=", 'http://blah.com')
+ project.should be_valid
+ project.send("#{attr}=", 'ftp://blah.com')
+ project.should_not be_valid
+ project.send("#{attr}=", 'blah.com')
+ project.should be_valid
+ project.send(attr).should == 'http://blah.com'
+ end
+ end
+
end |