Permalink
Browse files

Feature: merge artists

  • Loading branch information...
1 parent acad4a2 commit 92ffc25ea8602b0ee036ea6f27dfc09cc62d643c @knoopx committed Jul 7, 2012
View
@@ -26,6 +26,9 @@ gem 'jquery-rails'
gem 'prototype-rails'
gem 'capistrano'
+gem 'rspec-rails'
+gem 'database_cleaner'
+gem 'fuubar'
gem 'ruby-taglib2', :require => 'taglib2'
gem 'babosa'
View
@@ -54,12 +54,18 @@ GEM
sass (~> 3.1)
compass-rails (1.0.3)
compass (>= 0.12.2, < 0.14)
+ database_cleaner (0.8.0)
+ diff-lcs (1.1.3)
erubis (2.7.0)
execjs (1.4.0)
multi_json (~> 1.0)
formtastic (2.2.1)
actionpack (>= 3.0)
fssm (0.2.9)
+ fuubar (1.0.0)
+ rspec (~> 2.0)
+ rspec-instafail (~> 0.2.0)
+ ruby-progressbar (~> 0.0.10)
haml (3.1.6)
haml-rails (0.3.4)
actionpack (~> 3.0)
@@ -140,6 +146,21 @@ GEM
json (~> 1.4)
responders (0.9.1)
railties (~> 3.1)
+ rspec (2.10.0)
+ rspec-core (~> 2.10.0)
+ rspec-expectations (~> 2.10.0)
+ rspec-mocks (~> 2.10.0)
+ rspec-core (2.10.1)
+ rspec-expectations (2.10.0)
+ diff-lcs (~> 1.1.3)
+ rspec-instafail (0.2.4)
+ rspec-mocks (2.10.1)
+ rspec-rails (2.10.1)
+ actionpack (>= 3.0)
+ activesupport (>= 3.0)
+ railties (>= 3.0)
+ rspec (~> 2.10.0)
+ ruby-progressbar (0.0.10)
ruby-taglib2 (1.02)
sass (3.1.20)
sass-rails (3.2.5)
@@ -169,7 +190,9 @@ DEPENDENCIES
capistrano
coffee-rails
compass-rails
+ database_cleaner
formtastic
+ fuubar
haml-rails
has_scope
hpricot
@@ -183,6 +206,7 @@ DEPENDENCIES
nokogiri
prototype-rails
rails (~> 3.2)
+ rspec-rails
ruby-taglib2
sass-rails
uglifier
@@ -13,6 +13,16 @@ class ArtistsController < InheritedResources::Base
has_scope :genres_id_in, :type => :array
has_scope :genres_id_not_in, :type => :array
+ def merge
+ @artists = Artist.find(params[:artist_ids])
+
+ if params[:primary_artist_id]
+ @primary_artist = Artist.find(params[:primary_artist_id])
+ @primary_artist.merge_with(@artists)
+ redirect_to @primary_artist
+ end
+ end
+
def update_metadata
resource.update_metadata
resource.update_similar_artists
View
@@ -14,6 +14,8 @@ class Artist
field :lastfm_url
field :mbid
+ field :mbids, :type => Array, :default => []
+
field :similar_mbids, :type => Array, :default => []
field :images, :type => Hash, :default => {}
@@ -42,9 +44,41 @@ class Artist
search_scope :name_like
search_scope :tagged
- before_save :normalize_name
after_create :update_metadata, :update_similar_artists
+ def merge_with(artists)
+ [*artists].each do |artist|
+ next if artist.id == self.id
+
+ artist.releases.each do |release|
+ release.artists.delete(artist)
+ release.artists << self
+ end
+
+ artist.tracks.each do |track|
+ track.artist = self
+ track.save
+ end
+
+ self.tags_array += artist.tags_array
+ self.mbids << artist.mbid
+ artist.reload.destroy
+ end
+
+ self.tags_array.reject!(&:blank?)
+ self.tags_array.compact!
+ self.mbids.reject!(&:blank?)
+ self.mbids.uniq!
+
+ self.favorited_at = artists.map(&:favorited_at).compact.sort.first
+ self.save
+
+ # TODO
+ #self.class.reset_counters(self.id, :tracks)
+ #self.class.reset_counters(self.id, :releases)
+ end
+
+
def random_track
releases.map(&:tracks).flatten.sample
end
@@ -105,18 +139,4 @@ def update_similar_artists
end
true
end
-
- class << self
- def reset_counters!
- self.all.each do |artist|
- self.reset_counters(artist.id, :tracks)
- end
- end
- end
-
- protected
-
- def normalize_name
- self.normalized_name = self.name.to_slug.normalize.to_s
- end
end
View
@@ -31,8 +31,8 @@ class Release
has_and_belongs_to_many :artists
has_many :tracks, :dependent => :destroy, :order => :number.asc
- validates_uniqueness_of :path
- before_create :process_release
+ validates_uniqueness_of :path, :unless => lambda { self.path.blank? }
+ before_create :process_release, :unless => lambda { self.path.blank? }
after_create :update_metadata
scope :recent, lambda { |limit| desc(:created_at).limit(limit) }
@@ -108,6 +108,8 @@ def path_uri
end
def update_metadata
+ return if self.title.blank? or self.artists.none?
+
Rails.queue.push do
response = Nestful.get("http://ws.audioscrobbler.com/2.0/", :format => :json, :params => {
:api_key => "b25b959554ed76058ac220b7b2e0a026",
@@ -9,28 +9,33 @@
= f.input :tagged, :input_html => {:size => 50}
= f.submit "Search"
-%table
- %thead
- %tr
- %th= sort_link(:name)
- %th Tags
- %th= sort_link(:tracks_count)
- %th= sort_link(:play_count)
- %th= sort_link(:listeners)
- %th= sort_link(:created_at)
- %th
- %tbody
- - collection.each do |resource|
+= form_tag(merge_artists_path) do
+ %table
+ %thead
%tr
- %td= link_to resource.name, resource, :class => "title"
- %td= raw resource.tags_array.take(10).map { |tag| link_to tag, artists_path(:search => {:tagged => tag}) }.to_sentence
- %td= resource.tracks_count
- %td= resource.play_count
- %td= resource.listeners
- %td= time_ago_in_words(resource.created_at)
- %td.actions
- = play_link([resource, :tracks])
- = enqueue_link([resource, :tracks])
- = favorite_link(resource)
+ %th= sort_link(:name)
+ %th Tags
+ %th= sort_link(:tracks_count)
+ %th= sort_link(:play_count)
+ %th= sort_link(:listeners)
+ %th= sort_link(:created_at)
+ %th
+ %tbody
+ - collection.each do |resource|
+ %tr
+ %td
+ = check_box_tag "artist_ids[]", resource.id
+ = link_to resource.name, resource, :class => "title"
+ %td= raw resource.tags_array.take(10).map { |tag| link_to tag, artists_path(:search => {:tagged => tag}) }.to_sentence
+ %td= resource.tracks_count
+ %td= resource.play_count
+ %td= resource.listeners
+ %td= time_ago_in_words(resource.created_at)
+ %td.actions
+ = play_link([resource, :tracks])
+ = enqueue_link([resource, :tracks])
+ = favorite_link(resource)
+
+ = submit_tag "Merge selected"
= paginate(collection)
@@ -0,0 +1,15 @@
+.page-header
+ %strong Merge Artists
+
+.wrapper
+ = form_tag(merge_artists_path) do
+ %ul
+ - @artists.each do |artist|
+ %li
+ = hidden_field_tag "artist_ids[]", artist.id
+ = radio_button_tag "primary_artist_id", artist.id
+ = artist.name
+
+
+ %br
+ = submit_tag "Merge artists"
View
@@ -1,7 +1,10 @@
Jukebox2::Application.routes.draw do
resources :artists do
+ post :merge, :on => :collection
+
get :toggle_favorite, :on => :member
get :update_metadata, :on => :member
+
resources :tracks
end
@@ -7,7 +7,7 @@ module ClassMethods
def apply_pagination(opts = {})
scoped(opts.merge(:only => :index)) do |target|
if parent?
- target
+ target.limit(1000)
else
target.page(params.fetch(:page, 1))
end
View
@@ -15,8 +15,10 @@ class Railtie < Rails::Railtie
initializer :activate_queue_consumer do |app|
puts "Starting queue"
app.queue = Queue.new
- app.queue_consumer = ThreadedConsumer.start(app.queue)
- #at_exit { app.queue_consumer.shutdown }
+ unless Rails.env.test?
+ app.queue_consumer = ThreadedConsumer.start(app.queue)
+ at_exit { app.queue_consumer.shutdown }
+ end
end
end
View
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe Artist do
+ context "#merge_with" do
+ let!(:artist_one) do
+ Artist.create!(
+ :name => "Artist 1",
+ :mbid => "mbid-1",
+ :tags_array => ["hardcore", "punk"],
+ :normalized_name => "artist-1",
+ :releases => [Release.new(:name => "Release 1")],
+ :favorited_at => 1.day.ago
+ )
+ end
+
+ let!(:artist_two) do
+ Artist.create!(
+ :name => "Artist 2",
+ :mbid => "mbid-2",
+ :tags_array => ["hardcore", "screamo"],
+ :normalized_name => "artist-2",
+ :releases => [Release.new(:name => "Release 2")],
+ :favorited_at => 2.day.ago
+ )
+ end
+
+ example do
+ artist_one.releases.size.should == 1
+ artist_two.releases.size.should == 1
+
+ artist_one.merge_with(artist_two)
+
+ artist_one.releases.size.should == 2
+ artist_one.tags_array.should == ["hardcore", "punk", "screamo"]
+ artist_one.favorited_at.should == artist_two.favorited_at
+ artist_one.mbids.should be_include(artist_two.mbid)
+
+ lambda { artist_two.reload }.should raise_exception(Mongoid::Errors::DocumentNotFound)
+ end
+ end
+end
View
@@ -0,0 +1,25 @@
+# This file is copied to spec/ when you run 'rails generate rspec:install'
+ENV["RAILS_ENV"] ||= 'test'
+require File.expand_path("../../config/environment", __FILE__)
+require 'rspec/rails'
+require 'rspec/autorun'
+
+# Requires supporting ruby files with custom matchers and macros, etc,
+# in spec/support/ and its subdirectories.
+Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
+
+RSpec.configure do |config|
+ # If true, the base class of anonymous controllers will be inferred
+ # automatically. This will be the default behavior in future versions of
+ # rspec-rails.
+ config.infer_base_class_for_anonymous_controllers = false
+
+ config.before(:each) do
+ DatabaseCleaner.strategy = :truncation
+ DatabaseCleaner.start
+ end
+
+ config.after(:each) do
+ DatabaseCleaner.clean
+ end
+end

0 comments on commit 92ffc25

Please sign in to comment.