Skip to content

Commit

Permalink
Merge remote branch 'jtrupiano/yank_1.3.6'
Browse files Browse the repository at this point in the history
  • Loading branch information
qrush committed Feb 27, 2010
2 parents 8d003fc + 364e289 commit fc6d993
Show file tree
Hide file tree
Showing 24 changed files with 422 additions and 53 deletions.
28 changes: 24 additions & 4 deletions app/controllers/api/v1/rubygems_controller.rb
@@ -1,9 +1,9 @@
class Api::V1::RubygemsController < ApplicationController
skip_before_filter :verify_authenticity_token, :only => :create
skip_before_filter :verify_authenticity_token, :only => [:create, :yank]

before_filter :authenticate_with_api_key, :only => :create
before_filter :verify_authenticated_user, :only => :create
before_filter :find_gem, :only => :show
before_filter :authenticate_with_api_key, :only => [:create, :yank]
before_filter :verify_authenticated_user, :only => [:create, :yank]
before_filter :find_gem, :only => [:show, :yank]

def show
if @rubygem.hosted?
Expand All @@ -21,4 +21,24 @@ def create
gemcutter.process
render :text => gemcutter.message, :status => gemcutter.code
end

def yank
if !@rubygem.hosted?
render :json => "This gem does not exist.", :status => :not_found
elsif !@rubygem.owned_by?(current_user)
render :json => "You do not have permission to yank this gem.", :status => :forbidden
else
begin
version = Version.find_from_slug!(@rubygem, params[:version])
if version.indexed?
@rubygem.yank!(version)
render :json => "Successfully yanked"
else
render :json => "The version #{params[:version]} has already been yanked.", :status => :unprocessable_entity
end
rescue ActiveRecord::RecordNotFound
render :json => "The version #{params[:version]} does not exist.", :status => :not_found
end
end
end
end
26 changes: 21 additions & 5 deletions app/models/rubygem.rb
Expand Up @@ -18,16 +18,25 @@ def latest
validates_presence_of :name
validates_uniqueness_of :name

named_scope :with_versions, :conditions => ["versions_count > 0"]
named_scope :with_one_version, :conditions => ["versions_count = 1"]
named_scope :with_versions,
:select => 'DISTINCT rubygems.*',
:joins => :versions
named_scope :with_one_version,
:select => 'rubygems.*',
:joins => :versions,
:group => column_names.map{ |name| "rubygems.#{name}" }.join(', '),
:having => 'COUNT(versions.id) = 1'

named_scope :name_is, lambda { |name| {
:conditions => ["name = ?", name.strip],
:limit => 1 }
}

named_scope :search, lambda { |query| {
:conditions => ["(upper(name) like upper(:query) or upper(versions.description) like upper(:query)) and versions_count > 0",
:conditions => ["(upper(name) like upper(:query) or upper(versions.description) like upper(:query))",
{:query => "%#{query.strip}%"}],
:include => [:versions],
:having => 'count(versions.id) > 0',
:order => "rubygems.downloads desc" }
}

Expand Down Expand Up @@ -114,7 +123,7 @@ def with_downloads
end

def pushable?
new_record? || versions_count.zero?
new_record? || versions.indexed.count.zero?
end

def create_ownership(user)
Expand Down Expand Up @@ -157,13 +166,20 @@ def reorder_versions

self.versions.update_all(:latest => false)

if first_release = versions.release.first
if first_release = versions.indexed.release.first
versions.find_all_by_number(first_release.number).each do |version|
version.update_attributes(:latest => true)
end
end
end
end

def yank!(version)
version.yank!
if versions(true).indexed.count == 0
ownerships.each(&:destroy_without_callbacks)
end
end

def find_or_initialize_version_from_spec(spec)
version = self.versions.find_or_initialize_by_number_and_platform(spec.version.to_s, spec.original_platform.to_s)
Expand Down
18 changes: 14 additions & 4 deletions app/models/version.rb
Expand Up @@ -3,7 +3,7 @@ class Version < ActiveRecord::Base

default_scope :order => 'position'

belongs_to :rubygem, :counter_cache => true
belongs_to :rubygem
has_many :dependencies, :dependent => :destroy
has_many :downloads, :dependent => :destroy

Expand All @@ -18,9 +18,11 @@ class Version < ActiveRecord::Base
:order => 'created_at desc' }
}

named_scope :with_associated, { :conditions => ["rubygems.versions_count > 1"],
:include => :rubygem,
:order => "versions.built_at desc" }
named_scope :with_associated, {
:conditions => ["versions.rubygem_id IN (SELECT versions.rubygem_id FROM versions GROUP BY versions.rubygem_id HAVING COUNT(versions.id) > 1)"],
:include => :rubygem,
:order => "versions.built_at desc"
}
named_scope :latest, { :conditions => { :latest => true }}
named_scope :with_deps, { :include => {:dependencies => :rubygem} }
named_scope :prerelease, { :conditions => { :prerelease => true }}
Expand Down Expand Up @@ -84,6 +86,14 @@ def update_prerelease
def reorder_versions
rubygem.reorder_versions
end

def yank!
update_attributes!({:indexed => false})
end

def yanked?
!indexed
end

def info
[ description, summary, "This rubygem does not have a description or summary." ].detect(&:present?)
Expand Down
20 changes: 13 additions & 7 deletions app/views/rubygems/show.html.erb
@@ -1,7 +1,7 @@
<% @title = @rubygem.name %>
<% @subtitle = @latest_version.try(:slug) %>
<% if @rubygem.versions_count.zero? %>
<% if @rubygem.versions.count.zero? %>
<p>
This gem is not currently hosted on Gemcutter.
</p>
Expand All @@ -13,10 +13,16 @@
</div>

<div class="border">
<%= clippy(@latest_version.to_install) %>
<div class="install">
<div class="instructions"><strong>install</strong><span><%= @latest_version.to_install %></span></div>
</div>
<% if !@latest_version.yanked? -%>
<%= clippy(@latest_version.to_install) %>
<div class="install">
<div class="instructions"><strong>install</strong><span><%= @latest_version.to_install %></span></div>
</div>
<% else -%>
<div class="install">
This gem has been yanked.
</div>
<% end -%>
</div>

<div class="border">
Expand Down Expand Up @@ -66,8 +72,8 @@
<ol>
<%= render @rubygem.versions.limited(5) %>
</ol>
<% if @rubygem.versions_count > 5 %>
<%= link_to "Show all versions (#{@rubygem.versions_count} total)", rubygem_versions_url(@rubygem), :class => "more" %>
<% if @rubygem.versions.count > 5 %>
<%= link_to "Show all versions (#{@rubygem.versions.count} total)", rubygem_versions_url(@rubygem), :class => "more" %>
<% end %>
</div>

Expand Down
3 changes: 3 additions & 0 deletions app/views/versions/_version.html.erb
Expand Up @@ -4,4 +4,7 @@
<% if version.platformed? %>
<span class="platform"><%= version.platform %></span>
<% end %>
<% if version.yanked? -%>
<span class="yanked">yanked</span>
<% end -%>
</li>
1 change: 1 addition & 0 deletions config/routes.rb
Expand Up @@ -12,6 +12,7 @@
:member => {:reset => :put}
v1.resources :rubygems,
:as => "gems",
:member => {:yank => :delete},
:only => [:create, :show] do |rubygems|
v1.resource :migrate,
:only => [:create, :update],
Expand Down
@@ -0,0 +1,9 @@
class RemoveVersionsCountFromRubygems < ActiveRecord::Migration
def self.up
remove_column :rubygems, :versions_count
end

def self.down
add_column :rubygems, :versions_count, :integer, :default => 0
end
end
5 changes: 2 additions & 3 deletions db/schema.rb
Expand Up @@ -9,7 +9,7 @@
#
# It's strongly recommended to check this file into your version control system.

ActiveRecord::Schema.define(:version => 20091229014120) do
ActiveRecord::Schema.define(:version => 20100220111111) do

create_table "delayed_jobs", :force => true do |t|
t.integer "priority", :default => 0
Expand Down Expand Up @@ -79,9 +79,8 @@
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "downloads", :default => 0
t.integer "downloads", :default => 0
t.string "slug"
t.integer "versions_count", :default => 0
end

add_index "rubygems", ["name"], :name => "index_rubygems_on_name"
Expand Down
16 changes: 0 additions & 16 deletions features/delete.feature

This file was deleted.

27 changes: 25 additions & 2 deletions features/step_definitions/api_steps.rb
Expand Up @@ -16,8 +16,31 @@
visit api_v1_rubygems_path, :post, File.open(path).read
end

When /^I delete the gem "([^\"]*)" with my api key$/ do |arg1|
pending
When /^I yank the gem "([^\"]*)" version "([^\"]*)" with my api key$/ do |name, version_number|
header("HTTP_AUTHORIZATION", @api_key)
visit yank_api_v1_rubygem_path(name, :version => version_number), :delete
assert_match /Successfully yanked/, response.body
end

When /^I attempt to yank the gem "([^\"]*)" version "([^\"]*)" with my api key$/ do |name, version_number|
header("HTTP_AUTHORIZATION", @api_key)
visit yank_api_v1_rubygem_path(name, :version => version_number), :delete
end

When /^I migrate the gem "([^\"]*)" with my api key$/ do |name|
rubygem = Rubygem.find_by_name!(name)

header("HTTP_AUTHORIZATION", @api_key)
visit migrate_path(:rubygem_id => rubygem.to_param), :post
token = response.body

subdomain = rubygem.versions.latest.rubyforge_project

FakeWeb.register_uri(:get,
"http://#{subdomain}.rubyforge.org/migrate-#{name}.html",
:body => token)

visit migrate_path(:rubygem_id => rubygem.to_param), :put
end

When /^I list the owners of gem "([^\"]*)" with my api key$/ do |name|
Expand Down
10 changes: 10 additions & 0 deletions features/step_definitions/gem_steps.rb
Expand Up @@ -34,6 +34,16 @@
end


Given /^the gem "([^\"]*)" with version "([^\"]*)" has been indexed$/ do |name, version|
rubygem = Rubygem.find_by_name!(name)
rubygem.versions.find_by_number(version).update_attribute(:indexed, true)
end

Given /^I have have already yanked the gem "([^\"]*)" with version "([^\"]*)" with my api key$/ do |name, version|
rubygem = Rubygem.find_by_name!(name)
rubygem.versions.find_by_number(version).yank!
end

def build_gemspec(gemspec)
builder = Gem::Builder.new(gemspec)
builder.ui = Gem::SilentUI.new
Expand Down
4 changes: 4 additions & 0 deletions features/step_definitions/webrat_steps.rb
Expand Up @@ -101,6 +101,10 @@
assert_not_contain Regexp.new(text)
end

Then /^I should see the version "([^\"]*)" featured$/ do |version_number|
assert_select("h3", :text => version_number)
end

Then /^the "([^\"]*)" field should contain "([^\"]*)"$/ do |field, value|
# field_labeled(field).value.should =~ /#{value}/
assert field_labeled(field).value =~ /#{value}/
Expand Down
76 changes: 76 additions & 0 deletions features/yank.feature
@@ -0,0 +1,76 @@
Feature: Delete Gems
In order to remove my botched release
As a rubygem developer
I want to delete gems from Gemcutter

@wip
Scenario: User yanks a gem
Given I am signed up and confirmed as "email@person.com/password"
And I have a gem "RGem" with version "1.2.2"
And I have a gem "RGem" with version "1.2.3"
And I have an api key for "email@person.com/password"
And I've already pushed the gem "RGem-1.2.2.gem" with my api key
And the gem "RGem" with version "1.2.2" has been indexed
And I've already pushed the gem "RGem-1.2.3.gem" with my api key
And the gem "RGem" with version "1.2.3" has been indexed
When I yank the gem "RGem" version "1.2.3" with my api key
And I go to the dashboard with my api key
Then I should see "RGem"
And I visit the gem page for "RGem" version "1.2.3"
Then I should see "This gem has been yanked."
And I visit the gem page for "RGem"
Then I should see the version "1.2.2" featured

Scenario: User yanks the last version of a gem
Given I am signed up and confirmed as "old@owner.com/password"
And I have a gem "RGem" with version "1.2.3"
And I have an api key for "old@owner.com/password"
And I've already pushed the gem "RGem-1.2.3.gem" with my api key
And the gem "RGem" with version "1.2.3" has been indexed
When I yank the gem "RGem" version "1.2.3" with my api key
And I visit the gem page for "RGem"
And I should see "This gem has been yanked."

When I am signed up and confirmed as "new@owner.com/password"
And I have a gem "RGem" with version "0.1.0"
And I have an api key for "new@owner.com/password"
When I push the gem "RGem-0.1.0.gem" with my api key
And I visit the gem page for "RGem"
Then I should see "RGem"
And I should see "0.1.0"
When I list the owners of gem "RGem" with my api key
Then I should see "new@owner.com"
And I should not see "old@owner.com"

Scenario: User who is not owner attempts to yank a gem
Given I am signed up and confirmed as "non@owner.org/password"
And a user exists with an email of "the@owner.org"
And I have an api key for "non@owner.org/password"
And a rubygem exists with a name of "RGem"
And a version exists for the "RGem" rubygem with a number of "1.2.3"
And the "RGem" rubygem is owned by "the@owner.org"
And the gem "RGem" with version "1.2.3" has been indexed
When I attempt to yank the gem "RGem" version "1.2.3" with my api key
Then I should see "You do not have permission to yank this gem."

Scenario: User attempts to yank a nonexistent version of a gem
Given I am signed up and confirmed as "the@owner.com/password"
And I have a gem "RGem" with version "1.2.3"
And I have an api key for "the@owner.com/password"
And I've already pushed the gem "RGem-1.2.3.gem" with my api key
And the gem "RGem" with version "1.2.3" has been indexed
When I attempt to yank the gem "RGem" version "1.2.4" with my api key
Then I should see "The version 1.2.4 does not exist."

Scenario: User attempts to yank a gem that has already been yanked
Given I am signed up and confirmed as "the@owner.com/password"
And I have a gem "RGem" with version "1.2.2"
And I have a gem "RGem" with version "1.2.3"
And I have an api key for "the@owner.com/password"
And I've already pushed the gem "RGem-1.2.2.gem" with my api key
And the gem "RGem" with version "1.2.2" has been indexed
And I've already pushed the gem "RGem-1.2.3.gem" with my api key
And the gem "RGem" with version "1.2.3" has been indexed
And I have have already yanked the gem "RGem" with version "1.2.3" with my api key
When I attempt to yank the gem "RGem" version "1.2.3" with my api key
Then I should see "The version 1.2.3 has already been yanked"
1 change: 1 addition & 0 deletions gem/Rakefile
Expand Up @@ -41,6 +41,7 @@ begin
gem push merged into RubyGems 1.3.6
gem owner merged into RubyGems 1.3.6
gem webhook register urls to be pinged when gems are pushed
gem yank remove a specific version of a gem from Gemcutter
========================================================================
Expand Down
2 changes: 1 addition & 1 deletion gem/lib/gemcutter.rb
@@ -1,6 +1,6 @@
require 'rubygems/command_manager'

%w[migrate tumble webhook].each do |command|
%w[migrate tumble webhook yank].each do |command|
require "rubygems/commands/#{command}"
Gem::CommandManager.instance.register_command command.to_sym
end

0 comments on commit fc6d993

Please sign in to comment.