mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-24 11:10:12 +01:00
This changeset is larger than I prefer, and may be unstable with databases besides mysql.
Also, for developers: two new gems are required for running tests: zentest and flexmock. I applied James Kebinger's patch to add starred actions. These are implemented behind the scenes as a tag, so you can see all starred actions the way you would look at actions for any tag. Closes #387. Thanks, James! Tests now rely the ZenTest gem. Thanks Ryan Davis & Eric Hodel. I improved test coverage of a few models and created a test for the new helper methods to support the stars. (Helper method tests are made possible by ZenTest. The helper tests use mock objects to isolate them, courtesy of flexmock. Thanks, Jim Weirich!) Modified a few selenium tests to work properly with mysql. Upgraded the has_many_polymorphs plugin. Add rails_rcov plugin to get test coverage numbers more easily. Convert toggle_check action to correspond to a PUT instead of a POST (follows CRUD<->HTTP mapping better). I'm having some issues running tests with sqlite3 that I haven't been able to figure out. I'll work on it, but wanted to check in so I can check out and work from the beach this weekend. Happy holiday weekend to those of you in the U.S.! git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@544 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
parent
2ac8cd3324
commit
1cce7f076c
83 changed files with 2249 additions and 720 deletions
|
|
@ -8,3 +8,9 @@ require 'rake/testtask'
|
|||
require 'rake/rdoctask'
|
||||
|
||||
require 'tasks/rails'
|
||||
|
||||
begin
|
||||
require 'test/rails/rake_tasks'
|
||||
rescue LoadError => e
|
||||
#It's ok if you don't have ZenTest installed if you're not a developer
|
||||
end
|
||||
|
|
@ -7,7 +7,7 @@ require "redcloth"
|
|||
|
||||
require 'date'
|
||||
require 'time'
|
||||
Tag # We need this in development mode, or you get 'method missing' errors
|
||||
#Tag # We need this in development mode, or you get 'method missing' errors
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ class TodosController < ApplicationController
|
|||
skip_before_filter :login_required, :only => [:index]
|
||||
prepend_before_filter :login_or_feed_token_required, :only => [:index]
|
||||
append_before_filter :init, :except => [ :destroy, :completed, :completed_archive, :check_deferred ]
|
||||
append_before_filter :get_todo_from_params, :only => [ :edit, :toggle_check, :show, :update, :destroy ]
|
||||
append_before_filter :get_todo_from_params, :only => [ :edit, :toggle_check, :toggle_star, :show, :update, :destroy ]
|
||||
|
||||
session :off, :only => :index, :if => Proc.new { |req| is_feed_request(req) }
|
||||
|
||||
|
|
@ -138,6 +138,11 @@ class TodosController < ApplicationController
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def toggle_star
|
||||
@todo.toggle_star!
|
||||
@saved = @todo.save!
|
||||
end
|
||||
|
||||
def update
|
||||
@todo.tag_with(params[:tag_list],@user) if params[:tag_list]
|
||||
|
|
|
|||
|
|
@ -24,6 +24,17 @@ module TodosHelper
|
|||
str
|
||||
end
|
||||
|
||||
def remote_star_icon
|
||||
str = link_to( image_tag_for_star(@todo),
|
||||
toggle_star_todo_path(@todo),
|
||||
:class => "icon star_item", :title => "star the action '#{@todo.description}'")
|
||||
apply_behavior '.item-container a.star_item:click',
|
||||
remote_function(:url => javascript_variable('this.href'), :method => 'put',
|
||||
:with => "{ _source_view : '#{@source_view}' }"),
|
||||
:prevent_default => true
|
||||
str
|
||||
end
|
||||
|
||||
def remote_edit_icon
|
||||
if !@todo.completed?
|
||||
str = link_to( image_tag_for_edit,
|
||||
|
|
@ -41,8 +52,8 @@ module TodosHelper
|
|||
def remote_toggle_checkbox
|
||||
str = check_box_tag('item_id', toggle_check_todo_path(@todo), @todo.completed?, :class => 'item-checkbox')
|
||||
apply_behavior '.item-container input.item-checkbox:click',
|
||||
remote_function(:url => javascript_variable('this.value'),
|
||||
:with => "{ method : 'post', _source_view : '#{@source_view}' }")
|
||||
remote_function(:url => javascript_variable('this.value'), :method => 'put',
|
||||
:with => "{ _source_view : '#{@source_view}' }")
|
||||
str
|
||||
end
|
||||
|
||||
|
|
@ -57,7 +68,8 @@ module TodosHelper
|
|||
end
|
||||
|
||||
def tag_list
|
||||
@todo.tags.collect{|t| "<span class=\"tag\">" + link_to(t.name, :action => "tag", :id => t.name) + "</span>"}.join('')
|
||||
tag_list = @todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME}.collect{|t| "<span class=\"tag\">" + link_to(t.name, :action => "tag", :id => t.name) + "</span>"}.join('')
|
||||
"<span class='tags'>#{tag_list}</span>"
|
||||
end
|
||||
|
||||
def deferred_due_date
|
||||
|
|
@ -204,4 +216,9 @@ module TodosHelper
|
|||
image_tag("blank.png", :title =>"Edit action", :class=>"edit_item", :id=> dom_id(@todo, 'edit_icon'))
|
||||
end
|
||||
|
||||
def image_tag_for_star(todo)
|
||||
class_str = todo.starred? ? "starred_todo" : "unstarred_todo"
|
||||
image_tag("blank.png", :title =>"Star action", :class => class_str)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ class Todo < ActiveRecord::Base
|
|||
belongs_to :project
|
||||
belongs_to :user
|
||||
|
||||
STARRED_TAG_NAME = "starred"
|
||||
|
||||
acts_as_state_machine :initial => :active, :column => 'state'
|
||||
|
||||
state :active, :enter => Proc.new { |t| t[:show_from] = nil }
|
||||
|
|
@ -89,5 +91,21 @@ class Todo < ActiveRecord::Base
|
|||
:description => "Actions for #{user.display_name}"
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
def starred?
|
||||
tags.any? {|tag| tag.name == STARRED_TAG_NAME}
|
||||
end
|
||||
|
||||
def toggle_star!
|
||||
if starred?
|
||||
delete_tags STARRED_TAG_NAME
|
||||
tags.reload
|
||||
else
|
||||
add_tag STARRED_TAG_NAME
|
||||
tags.reload
|
||||
end
|
||||
starred?
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
<%
|
||||
@todo = todo
|
||||
Tag
|
||||
%>
|
||||
<div id="<%= dom_id(todo) %>" class="item-container">
|
||||
<div id="<%= dom_id(todo, 'line') %>">
|
||||
<%= remote_delete_icon %>
|
||||
<%= remote_edit_icon %>
|
||||
<%= remote_star_icon %>
|
||||
<%= remote_toggle_checkbox unless source_view_is :deferred %>
|
||||
<div class="description<%= staleness_class( todo ) %>">
|
||||
<%= date_span %>
|
||||
|
|
|
|||
3
tracks/app/views/todos/toggle_star.rjs
Normal file
3
tracks/app/views/todos/toggle_star.rjs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
if @saved
|
||||
page[@todo].down('a.star_item').down('img').toggleClassName('starred_todo').toggleClassName('unstarred_todo')
|
||||
end
|
||||
|
|
@ -33,7 +33,7 @@ ActionController::Routing::Routes.draw do |map|
|
|||
|
||||
# ToDo Routes
|
||||
map.resources :todos,
|
||||
:member => {:toggle_check => :post},
|
||||
:member => {:toggle_check => :put, :toggle_star => :put},
|
||||
:collection => {:check_deferred => :post, :filter_to_context => :post, :filter_to_project => :post}
|
||||
map.with_options :controller => "todos" do |todos|
|
||||
todos.home '', :action => "index"
|
||||
|
|
|
|||
|
|
@ -22,5 +22,9 @@ class ActiveRecord::Base
|
|||
split = tag_string.downcase.split(", ")
|
||||
tags.delete tags.select{|t| split.include? t.name}
|
||||
end
|
||||
|
||||
def add_tag tag_name
|
||||
Tag.find_or_create_by_name(tag_name).on(self,user)
|
||||
end
|
||||
|
||||
end
|
||||
BIN
tracks/public/images/staricons.png
Normal file
BIN
tracks/public/images/staricons.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
|
|
@ -78,6 +78,11 @@ a:hover img.edit_item {background-image: url(../images/edit_on.png); background-
|
|||
img.delete_item {background-image: url(../images/delete_off.png); background-repeat: no-repeat; border: none;}
|
||||
a:hover img.delete_item {background-image: url(../images/delete_on.png);background-color: transparent;background-repeat: no-repeat; border: none;}
|
||||
|
||||
img.starred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: 0px 0px;}
|
||||
a:hover img.starred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: -16px 0px;}
|
||||
img.unstarred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: -32px 0px;}
|
||||
a:hover img.unstarred_todo {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: -48px 0px;}
|
||||
|
||||
a.to_top {background: transparent url(../images/top_off.png) no-repeat;}
|
||||
a.to_top:hover {background: transparent url(../images/top_on.png) no-repeat;}
|
||||
|
||||
|
|
@ -285,12 +290,12 @@ input.item-checkbox {
|
|||
}
|
||||
|
||||
.description {
|
||||
margin-left: 70px;
|
||||
margin-left: 85px;
|
||||
}
|
||||
|
||||
.stale_l1, .stale_l2, .stale_l3 {
|
||||
margin-left: 67px;
|
||||
padding-left: 3px;
|
||||
margin-left: 82px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
.stale_l1 {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ require 'backend_controller'
|
|||
# Re-raise errors caught by the controller.
|
||||
class BackendController; def rescue_action(e) raise e end; end
|
||||
|
||||
class BackendControllerTest < Test::Unit::TestCase
|
||||
class BackendControllerTest < Test::Rails::TestCase
|
||||
fixtures :users, :projects, :contexts, :todos, :notes
|
||||
|
||||
def setup
|
||||
|
|
|
|||
|
|
@ -150,4 +150,36 @@ class ContextsControllerTest < TodoContainerControllerTestBase
|
|||
assert_response :ok
|
||||
end
|
||||
|
||||
def test_show_sets_title
|
||||
@request.session['user_id'] = users(:admin_user).id
|
||||
get :show, { :id => "1" }
|
||||
assert_equal 'TRACKS::Context: agenda', assigns['page_title']
|
||||
end
|
||||
|
||||
def test_show_renders_show_template
|
||||
@request.session['user_id'] = users(:admin_user).id
|
||||
get :show, { :id => "1" }
|
||||
assert_template "contexts/show"
|
||||
end
|
||||
|
||||
def test_show_xml_renders_context_to_xml
|
||||
@request.session['user_id'] = users(:admin_user).id
|
||||
get :show, { :id => "1", :format => 'xml' }
|
||||
assert_equal contexts(:agenda).to_xml( :except => :user_id ), @response.body
|
||||
end
|
||||
|
||||
def test_show_with_nil_context_returns_404
|
||||
@request.session['user_id'] = users(:admin_user).id
|
||||
get :show, { :id => "0" }
|
||||
assert_equal 'Context not found', @response.body
|
||||
assert_response 404
|
||||
end
|
||||
|
||||
def test_show_xml_with_nil_context_returns_404
|
||||
@request.session['user_id'] = users(:admin_user).id
|
||||
get :show, { :id => "0", :format => 'xml' }
|
||||
assert_response 404
|
||||
assert_xml_select 'error', 'Context not found'
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ require 'data_controller'
|
|||
# Re-raise errors caught by the controller.
|
||||
class DataController; def rescue_action(e) raise e end; end
|
||||
|
||||
class DataControllerTest < Test::Unit::TestCase
|
||||
class DataControllerTest < Test::Rails::TestCase
|
||||
fixtures :users, :preferences, :projects, :notes
|
||||
|
||||
def setup
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ require 'feedlist_controller'
|
|||
# Re-raise errors caught by the controller.
|
||||
class FeedlistController; def rescue_action(e) raise e end; end
|
||||
|
||||
class FeedlistControllerTest < Test::Unit::TestCase
|
||||
class FeedlistControllerTest < Test::Rails::TestCase
|
||||
fixtures :users, :preferences, :projects, :contexts, :todos, :notes
|
||||
|
||||
def setup
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ require_dependency "login_system"
|
|||
# Re-raise errors caught by the controller.
|
||||
class LoginController; def rescue_action(e) raise e end; end
|
||||
|
||||
class LoginControllerTest < Test::Unit::TestCase
|
||||
class LoginControllerTest < Test::Rails::TestCase
|
||||
fixtures :preferences, :users
|
||||
|
||||
def setup
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ require 'notes_controller'
|
|||
# Re-raise errors caught by the controller.
|
||||
class NotesController; def rescue_action(e) raise e end; end
|
||||
|
||||
class NotesControllerTest < Test::Unit::TestCase
|
||||
class NotesControllerTest < Test::Rails::TestCase
|
||||
def setup
|
||||
@controller = NotesController.new
|
||||
request = ActionController::TestRequest.new
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ require 'preference'
|
|||
# Re-raise errors caught by the controller.
|
||||
class PreferencesController; def rescue_action(e) raise e end; end
|
||||
|
||||
class PreferencesControllerTest < Test::Unit::TestCase
|
||||
class PreferencesControllerTest < Test::Rails::TestCase
|
||||
fixtures :users
|
||||
|
||||
def setup
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class ProjectsControllerTest < TodoContainerControllerTestBase
|
|||
def test_create_project_and_go_to_project_page
|
||||
num_projects = Project.count
|
||||
xhr :post, :create, { :project => {:name => 'Immediate Project Planning Required'}, :go_to_project => 1}
|
||||
assert_js_redirected_to '/projects/5'
|
||||
assert_js_redirected_to %r{/?projects/\d+}
|
||||
assert_equal num_projects + 1, Project.count
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
class TodoContainerControllerTestBase < Test::Unit::TestCase
|
||||
class TodoContainerControllerTestBase < Test::Rails::TestCase
|
||||
|
||||
def perform_setup(container_class, controller_class)
|
||||
@controller = controller_class.new
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ require 'todos_controller'
|
|||
# Re-raise errors caught by the controller.
|
||||
class TodosController; def rescue_action(e) raise e end; end
|
||||
|
||||
class TodosControllerTest < Test::Unit::TestCase
|
||||
class TodosControllerTest < Test::Rails::TestCase
|
||||
fixtures :users, :preferences, :projects, :contexts, :todos, :tags, :taggings
|
||||
|
||||
def setup
|
||||
|
|
@ -25,6 +25,15 @@ class TodosControllerTest < Test::Unit::TestCase
|
|||
assert_equal 1, assigns['context_not_done_counts'][contexts(:lab).id]
|
||||
end
|
||||
|
||||
def test_tag_is_retrieved_properly
|
||||
@request.session['user_id'] = users(:admin_user).id
|
||||
get :index
|
||||
t = assigns['not_done_todos'].find{|t| t.id == 2}
|
||||
assert_equal 1, t.tags.count
|
||||
assert_equal 'foo', t.tags[0].name
|
||||
assert !t.starred?
|
||||
end
|
||||
|
||||
def test_not_done_counts_after_hiding_project
|
||||
p = Project.find(1)
|
||||
p.hide!
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ require 'users_controller'
|
|||
# Re-raise errors caught by the controller.
|
||||
class UsersController; def rescue_action(e) raise e end; end
|
||||
|
||||
class UsersControllerTest < Test::Unit::TestCase
|
||||
class UsersControllerTest < Test::Rails::TestCase
|
||||
fixtures :preferences, :users
|
||||
|
||||
def setup
|
||||
|
|
|
|||
5
tracks/test/selenium/home/star_todo.rsel
Normal file
5
tracks/test/selenium/home/star_todo.rsel
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
setup :fixtures => :all
|
||||
login :as => 'admin'
|
||||
open '/'
|
||||
click "css=#c1 #todo_9 a.star_item"
|
||||
wait_for_element_present "css=#c1 #todo_9 img.starred_todo"
|
||||
4
tracks/test/selenium/home/verify_item_not_starred.rsel
Normal file
4
tracks/test/selenium/home/verify_item_not_starred.rsel
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
setup :fixtures => :all
|
||||
login :as => 'admin'
|
||||
open '/'
|
||||
assert_element_present "css=#c2 #todo_2 img.unstarred_todo"
|
||||
|
|
@ -1,13 +1,20 @@
|
|||
setup :fixtures => :all
|
||||
next_available_todo_id = 18
|
||||
login :as => 'admin'
|
||||
|
||||
#first, defer a todo
|
||||
open "/projects/1"
|
||||
include_partial 'project_detail/add_deferred_todo'
|
||||
open "/projects/1"
|
||||
click "edit_icon_todo_#{next_available_todo_id}"
|
||||
wait_for_element_present "show_from_todo_#{next_available_todo_id}"
|
||||
type "show_from_todo_#{next_available_todo_id}", ""
|
||||
click "edit_icon_todo_5"
|
||||
wait_for_element_present "show_from_todo_5"
|
||||
type "show_from_todo_5", "1/1/2030"
|
||||
click "//input[@value='Update']"
|
||||
wait_for_element_present "xpath=//div[@id='p1'] //div[@id='todo_#{next_available_todo_id}']"
|
||||
wait_for_element_present "xpath=//div[@id='tickler'] //div[@id='todo_5']"
|
||||
|
||||
#now activate the other deferred one
|
||||
open "/projects/1"
|
||||
click "edit_icon_todo_15"
|
||||
wait_for_element_present "show_from_todo_15"
|
||||
type "show_from_todo_15", ""
|
||||
click "//input[@value='Update']"
|
||||
wait_for_element_present "xpath=//div[@id='p1'] //div[@id='todo_15']"
|
||||
assert_not_visible "tickler-empty-nd"
|
||||
assert_text 'badge_count', '3'
|
||||
assert_text 'badge_count', '2'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
ENV["RAILS_ENV"] = "test"
|
||||
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
||||
require File.expand_path(File.dirname(__FILE__) + "/../app/controllers/application")
|
||||
require 'test/rails' #you need the zentest gem isntalled
|
||||
require 'test_help'
|
||||
|
||||
require 'flexmock/test_unit' #and the flexmock gem, too!
|
||||
|
||||
module Tracks
|
||||
class Config
|
||||
def self.salt
|
||||
|
|
@ -11,6 +14,20 @@ module Tracks
|
|||
end
|
||||
|
||||
class Test::Unit::TestCase
|
||||
|
||||
def xml_document
|
||||
@xml_document ||= HTML::Document.new(@response.body, false, true)
|
||||
end
|
||||
|
||||
def assert_xml_select(*args, &block)
|
||||
@html_document = xml_document
|
||||
assert_select(*args, &block)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Test::Rails::TestCase < Test::Unit::TestCase
|
||||
|
||||
# Turn off transactional fixtures if you're working with MyISAM tables in MySQL
|
||||
self.use_transactional_fixtures = true
|
||||
|
||||
|
|
@ -30,16 +47,7 @@ class Test::Unit::TestCase
|
|||
end
|
||||
return string
|
||||
end
|
||||
|
||||
def xml_document
|
||||
@xml_document ||= HTML::Document.new(@response.body, false, true)
|
||||
end
|
||||
|
||||
def assert_xml_select(*args, &block)
|
||||
@html_document = xml_document
|
||||
assert_select(*args, &block)
|
||||
end
|
||||
|
||||
|
||||
def next_week
|
||||
1.week.from_now.utc.to_date
|
||||
end
|
||||
|
|
@ -58,6 +66,8 @@ class Test::Unit::TestCase
|
|||
|
||||
if options.is_a?(String)
|
||||
assert_equal(options.gsub(/^\//, ''), redirected_to, message)
|
||||
elsif options.is_a?(Regexp)
|
||||
assert(options =~ redirected_to, "#{message} #{options} #{redirected_to}")
|
||||
else
|
||||
msg = build_message(message, "response is not a redirection to all of the options supplied (redirection is <?>)", redirected_to)
|
||||
assert_equal(@controller.url_for(options).match(js_regexp)[3], redirected_to, msg)
|
||||
|
|
@ -69,7 +79,7 @@ class Test::Unit::TestCase
|
|||
end
|
||||
|
||||
class ActionController::IntegrationTest
|
||||
|
||||
|
||||
def assert_test_environment_ok
|
||||
assert_equal "test", ENV['RAILS_ENV']
|
||||
assert_equal "change-me", Tracks::Config.salt
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class ContextTest < Test::Unit::TestCase
|
||||
class ContextTest < Test::Rails::TestCase
|
||||
fixtures :contexts, :todos, :users, :preferences
|
||||
|
||||
def setup
|
||||
|
|
@ -107,4 +107,11 @@ class ContextTest < Test::Unit::TestCase
|
|||
assert_equal "<p>#{undone_todo_count}. Context is Hidden.</p>", @agenda.summary(undone_todo_count)
|
||||
end
|
||||
|
||||
def test_null_object
|
||||
c = Context.null_object
|
||||
assert c.nil?
|
||||
assert_nil c.id
|
||||
assert_equal '', c.name
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class NotesTest < Test::Unit::TestCase
|
||||
class NotesTest < Test::Rails::TestCase
|
||||
fixtures :notes
|
||||
|
||||
def setup
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class PreferenceTest < Test::Unit::TestCase
|
||||
class PreferenceTest < Test::Rails::TestCase
|
||||
fixtures :users, :preferences
|
||||
|
||||
def setup
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class ProjectTest < Test::Unit::TestCase
|
||||
class ProjectTest < Test::Rails::TestCase
|
||||
fixtures :projects, :contexts, :todos, :users, :preferences
|
||||
|
||||
def setup
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class TagTest < Test::Unit::TestCase
|
||||
class TagTest < Test::Rails::TestCase
|
||||
fixtures :tags
|
||||
|
||||
# Replace this with your real tests.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class TaggingTest < Test::Unit::TestCase
|
||||
class TaggingTest < Test::Rails::TestCase
|
||||
fixtures :taggings
|
||||
|
||||
# Replace this with your real tests.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'date'
|
||||
|
||||
class TodoTest < Test::Unit::TestCase
|
||||
fixtures :todos, :users, :contexts, :preferences
|
||||
class TodoTest < Test::Rails::TestCase
|
||||
fixtures :todos, :users, :contexts, :preferences, :tags, :taggings
|
||||
|
||||
def setup
|
||||
@not_completed1 = Todo.find(1).reload
|
||||
|
|
@ -139,4 +139,28 @@ class TodoTest < Test::Unit::TestCase
|
|||
assert_equal :deferred, t.current_state
|
||||
end
|
||||
|
||||
def test_todo_is_not_starred
|
||||
assert !@not_completed1.starred?
|
||||
end
|
||||
|
||||
def test_todo_2_is_not_starred
|
||||
assert !Todo.find(2).starred?
|
||||
end
|
||||
|
||||
def test_todo_is_starred_after_starred_tag_is_added
|
||||
@not_completed1.add_tag('starred')
|
||||
assert @not_completed1.starred?
|
||||
end
|
||||
|
||||
def test_todo_is_starred_after_toggle_starred
|
||||
@not_completed1.toggle_star!
|
||||
assert @not_completed1.starred?
|
||||
end
|
||||
|
||||
def test_todo_is_not_starred_after_toggle_starred_twice
|
||||
@not_completed1.toggle_star!
|
||||
@not_completed1.toggle_star!
|
||||
assert !@not_completed1.starred?
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class SimpleLdapAuthenticator
|
|||
end
|
||||
end
|
||||
|
||||
class UserTest < Test::Unit::TestCase
|
||||
class UserTest < Test::Rails::TestCase
|
||||
fixtures :users, :preferences, :projects, :contexts, :todos
|
||||
|
||||
def setup
|
||||
|
|
|
|||
40
tracks/test/views/todos_helper_test.rb
Normal file
40
tracks/test/views/todos_helper_test.rb
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class TodosHelperTest < Test::Rails::HelperTestCase
|
||||
|
||||
def setup
|
||||
super
|
||||
end
|
||||
|
||||
include TodosHelper
|
||||
|
||||
def test_remote_star_icon_unstarred
|
||||
@todo = flexmock(:id => 1, :to_param => 1, :description => 'Get gas', :starred? => false)
|
||||
assert_remote_star_icon_helper_matches %r{<a href="/todos/1;toggle_star" class="icon star_item" title="star the action 'Get gas'"><img alt="Blank" class="unstarred_todo" src="/images/blank.png[?0-9]*" title="Star action" /></a>}
|
||||
assert_behavior_registered
|
||||
end
|
||||
|
||||
def test_remote_star_icon_starred
|
||||
@todo = flexmock(:id => 1, :to_param => 1, :description => 'Get gas', :starred? => true)
|
||||
assert_remote_star_icon_helper_matches %r{<a href="/todos/1;toggle_star" class="icon star_item" title="star the action 'Get gas'"><img alt="Blank" class="starred_todo" src="/images/blank.png[?0-9]*" title="Star action" /></a>}
|
||||
assert_behavior_registered
|
||||
end
|
||||
|
||||
def assert_remote_star_icon_helper_matches(regex)
|
||||
@controller.send :initialise_js_behaviours #simulate before filter
|
||||
output = remote_star_icon
|
||||
#puts output
|
||||
assert output =~ regex
|
||||
@controller.send :store_js_behaviours #simulate after filter
|
||||
end
|
||||
|
||||
def assert_behavior_registered
|
||||
behaviors = @controller.session[:js_behaviours]
|
||||
assert behaviors[:options][:reapply_after_ajax]
|
||||
assert_equal 1, behaviors[:rules].length
|
||||
rule = behaviors[:rules][0]
|
||||
assert_equal ".item-container a.star_item:click", rule[0]
|
||||
assert_equal "new Ajax.Request(this.href, {asynchronous:true, evalScripts:true, method:'put', parameters:{ _source_view : '' }}); return false;",
|
||||
rule[1]
|
||||
end
|
||||
end
|
||||
43
tracks/vendor/plugins/has_many_polymorphs/Manifest
vendored
Normal file
43
tracks/vendor/plugins/has_many_polymorphs/Manifest
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
LICENSE
|
||||
Manifest
|
||||
README
|
||||
Rakefile
|
||||
init.rb
|
||||
lib/has_many_polymorphs/association.rb
|
||||
lib/has_many_polymorphs/autoload.rb
|
||||
lib/has_many_polymorphs/base.rb
|
||||
lib/has_many_polymorphs/class_methods.rb
|
||||
lib/has_many_polymorphs/configuration.rb
|
||||
lib/has_many_polymorphs/debugging_tools.rb
|
||||
lib/has_many_polymorphs/dependencies.rb
|
||||
lib/has_many_polymorphs/rake_task_redefine_task.rb
|
||||
lib/has_many_polymorphs/reflection.rb
|
||||
lib/has_many_polymorphs/support_methods.rb
|
||||
lib/has_many_polymorphs.rb
|
||||
test/fixtures/aquatic/fish.yml
|
||||
test/fixtures/aquatic/little_whale_pupils.yml
|
||||
test/fixtures/aquatic/whales.yml
|
||||
test/fixtures/bow_wows.yml
|
||||
test/fixtures/cats.yml
|
||||
test/fixtures/eaters_foodstuffs.yml
|
||||
test/fixtures/frogs.yml
|
||||
test/fixtures/keep_your_enemies_close.yml
|
||||
test/fixtures/petfoods.yml
|
||||
test/fixtures/wild_boars.yml
|
||||
test/models/aquatic/fish.rb
|
||||
test/models/aquatic/pupils_whale.rb
|
||||
test/models/aquatic/whale.rb
|
||||
test/models/beautiful_fight_relationship.rb
|
||||
test/models/cat.rb
|
||||
test/models/dog.rb
|
||||
test/models/eaters_foodstuff.rb
|
||||
test/models/frog.rb
|
||||
test/models/kitten.rb
|
||||
test/models/petfood.rb
|
||||
test/models/tabby.rb
|
||||
test/models/wild_boar.rb
|
||||
test/modules/extension_module.rb
|
||||
test/modules/other_extension_module.rb
|
||||
test/schema.rb
|
||||
test/test_helper.rb
|
||||
test/unit/polymorph_test.rb
|
||||
27
tracks/vendor/plugins/has_many_polymorphs/README
vendored
27
tracks/vendor/plugins/has_many_polymorphs/README
vendored
|
|
@ -1,17 +1,24 @@
|
|||
|
||||
Self-referential, polymorphic has_many :through helper
|
||||
Self-referential, polymorphic has_many :through helper for ActiveRecord.
|
||||
|
||||
Copyright 2006 Evan Weaver (see the LICENSE file)
|
||||
Copyright 2007 Cloudburst, LLC (see the LICENSE file)
|
||||
|
||||
"model :parent_class" may be required in some controllers or perhaps models in order for reloading to work properly, since the parent setup must be executed on the child every time the child class is reloaded.
|
||||
|
||||
Usage and help:
|
||||
http://blog.evanweaver.com/articles/2006/06/02/has_many_polymorphs
|
||||
|
||||
Also see the source code, although it's probably not going to be super helpful to you.
|
||||
Documentation:
|
||||
http://blog.evanweaver.com/pages/has_many_polymorphs
|
||||
|
||||
Changelog:
|
||||
|
||||
27.3. use new :source and :source_type options in 1.2.3 (David Lemstra); fix pluralization bug; add some tests; experimental tagging generator
|
||||
27.2. deprecate has_many_polymorphs_cache_classes= option, because it doesn't really work. use config.cache_classes= instead to cache all reloadable items
|
||||
27.1. dispatcher.to_prepare didn't fire in the console; now using a config.after_initialize wrapper instead
|
||||
27. dependency injection framework elimates having to care about load order
|
||||
26. make the logger act sane for the gem version
|
||||
25.2. allow :skip_duplicates on double relationships
|
||||
25.1. renamed :ignore_duplicates to :skip_duplicates to better express its non-passive behavior, made sure not to load target set on push unless necessary
|
||||
25. activerecord compatibility branch becomes trunk. extra options now supported for double polymorphism; conditions nulled-out and propogated to child relationships; more tests; new :ignore_duplicates option on macro can be set to false if you want << to try to push duplicate associations
|
||||
24.1. code split into multiple files. tests added for pluralization check. rails 1.1.6 no longer officially supported.
|
||||
24. unlimited mixed class association extensions for both single and double targets and joins
|
||||
23. gem version
|
||||
22. api change; prefix on methods is now singular when using :rename_individual_collections
|
||||
21. add configuration option to cache polymorphic classes in development mode
|
||||
20. collection methods (push, delete, clear) now on individual collections
|
||||
|
|
@ -40,7 +47,3 @@ Changelog:
|
|||
3. added :dependent support on the join table
|
||||
1-2. no changelog
|
||||
|
||||
Known problems:
|
||||
|
||||
1. Plugin's test fixtures do not load properly for non-edge postgres, invalidating the tests.
|
||||
2. quote_value() hack is stupid.
|
||||
59
tracks/vendor/plugins/has_many_polymorphs/Rakefile
vendored
Normal file
59
tracks/vendor/plugins/has_many_polymorphs/Rakefile
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
require 'rubygems'
|
||||
require 'rake'
|
||||
require 'lib/has_many_polymorphs/rake_task_redefine_task'
|
||||
|
||||
NAME = "has_many_polymorphs"
|
||||
|
||||
begin
|
||||
require 'rake/clean'
|
||||
gem 'echoe', '>= 1.1'
|
||||
require 'echoe'
|
||||
require 'fileutils'
|
||||
|
||||
AUTHOR = "Evan Weaver"
|
||||
EMAIL = "evan at cloudbur dot st"
|
||||
DESCRIPTION = "Self-referential, polymorphic has_many :through helper for ActiveRecord."
|
||||
CHANGES = `cat README`[/^([\d\.]+\. .*)/, 1]
|
||||
RUBYFORGE_NAME = "polymorphs"
|
||||
GEM_NAME = "has_many_polymorphs"
|
||||
HOMEPATH = "http://blog.evanweaver.com"
|
||||
RELEASE_TYPES = ["gem"]
|
||||
REV = nil
|
||||
VERS = `cat README`[/^([\d\.]+)\. /, 1]
|
||||
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
||||
RDOC_OPTS = ['--quiet', '--title', "has_many_polymorphs documentation",
|
||||
"--opname", "index.html",
|
||||
"--line-numbers",
|
||||
"--main", "README",
|
||||
"--inline-source"]
|
||||
|
||||
include FileUtils
|
||||
|
||||
echoe = Echoe.new(GEM_NAME, VERS) do |p|
|
||||
p.author = AUTHOR
|
||||
p.rubyforge_name = RUBYFORGE_NAME
|
||||
p.name = NAME
|
||||
p.description = DESCRIPTION
|
||||
p.changes = CHANGES
|
||||
p.email = EMAIL
|
||||
p.summary = DESCRIPTION
|
||||
p.url = HOMEPATH
|
||||
p.need_tar = false
|
||||
p.need_tar_gz = true
|
||||
p.test_globs = ["*_test.rb"]
|
||||
p.clean_globs = CLEAN
|
||||
end
|
||||
|
||||
rescue LoadError => boom
|
||||
puts "You are missing a dependency required for meta-operations on this gem."
|
||||
puts "#{boom.to_s.capitalize}."
|
||||
|
||||
desc 'Run the default tasks'
|
||||
task :default => :test
|
||||
end
|
||||
|
||||
desc 'Run the test suite.'
|
||||
Rake::Task.redefine_task("test") do
|
||||
puts "Notice; tests must be run from within a functioning Rails environment."
|
||||
system "ruby -Ibin:lib:test test/unit/polymorph_test.rb #{ENV['METHOD'] ? "--name=#{ENV['METHOD']}" : ""}"
|
||||
end
|
||||
12
tracks/vendor/plugins/has_many_polymorphs/TAGGING_INSTALL
vendored
Normal file
12
tracks/vendor/plugins/has_many_polymorphs/TAGGING_INSTALL
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
Thank you for installing has_many_polymorphs!
|
||||
|
||||
There is an experimental tagging system generator in place.
|
||||
|
||||
./script/generator tagging TaggableModel1 TaggableModel2 [..]
|
||||
|
||||
You can use the flags --skip-migration and/or --self-referential.
|
||||
|
||||
Tests will be generated, but will not work unless you have at
|
||||
least 2 fixture entries for each taggable model. Their ids must
|
||||
be 1 and 2.
|
||||
78
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/tagging_generator.rb
vendored
Normal file
78
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/tagging_generator.rb
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
require 'ruby-debug'
|
||||
Debugger.start
|
||||
|
||||
class TaggingGenerator < Rails::Generator::NamedBase
|
||||
default_options :skip_migration => false
|
||||
default_options :self_referential => false
|
||||
attr_reader :parent_association_name
|
||||
attr_reader :taggable_models
|
||||
|
||||
def initialize(runtime_args, runtime_options = {})
|
||||
@parent_association_name = (runtime_args.include?("--self-referential") ? "tagger" : "tag")
|
||||
@taggable_models = runtime_args.reject{|opt| opt =~ /^--/}.map do |taggable|
|
||||
":" + taggable.underscore.pluralize
|
||||
end
|
||||
@taggable_models += [":tags"] if runtime_args.include?("--self-referential")
|
||||
@taggable_models.uniq!
|
||||
|
||||
hacks
|
||||
|
||||
runtime_args.unshift("placeholder")
|
||||
super
|
||||
end
|
||||
|
||||
def hacks
|
||||
# add the extension require in environment.rb
|
||||
phrase = "require 'tagging_extensions'"
|
||||
filename = "#{RAILS_ROOT}/config/environment.rb"
|
||||
unless (open(filename) do |file|
|
||||
file.grep(/#{Regexp.escape phrase}/).any?
|
||||
end)
|
||||
open(filename, 'a+') do |file|
|
||||
file.puts "\n" + phrase + "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def manifest
|
||||
record do |m|
|
||||
m.class_collisions class_path, class_name, "#{class_name}Test"
|
||||
|
||||
m.directory File.join('app/models', class_path)
|
||||
m.directory File.join('test/unit', class_path)
|
||||
m.directory File.join('test/fixtures', class_path)
|
||||
m.directory File.join('test/fixtures', class_path)
|
||||
m.directory File.join('lib')
|
||||
|
||||
m.template 'tag.rb', File.join('app/models', class_path, "tag.rb")
|
||||
m.template 'tag_test.rb', File.join('test/unit', class_path, "tag_test.rb")
|
||||
m.template 'tags.yml', File.join('test/fixtures', class_path, "tags.yml")
|
||||
|
||||
m.template 'tagging.rb', File.join('app/models', class_path, "tagging.rb")
|
||||
m.template 'tagging_test.rb', File.join('test/unit', class_path, "tagging_test.rb")
|
||||
m.template 'taggings.yml', File.join('test/fixtures', class_path, "taggings.yml")
|
||||
|
||||
m.template 'tagging_extensions.rb', File.join('lib', 'tagging_extensions.rb')
|
||||
|
||||
unless options[:skip_migration]
|
||||
m.migration_template 'migration.rb', 'db/migrate',
|
||||
:migration_file_name => "create_tags_and_taggings"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def banner
|
||||
"Usage: #{$0} generate tagging [TaggableModelA TaggableModelB ...]"
|
||||
end
|
||||
|
||||
def add_options!(opt)
|
||||
opt.separator ''
|
||||
opt.separator 'Options:'
|
||||
opt.on("--skip-migration",
|
||||
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
|
||||
opt.on("--self-referential",
|
||||
"Allow tags to tag themselves.") { |v| options[:self_referential] = v }
|
||||
end
|
||||
end
|
||||
0
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/index.rhtml
vendored
Normal file
0
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/index.rhtml
vendored
Normal file
23
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/migration.rb
vendored
Normal file
23
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/migration.rb
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
class CreateTagsAndTaggings < ActiveRecord::Migration
|
||||
|
||||
def self.up
|
||||
create_table :tags do |t|
|
||||
t.column :name, :string, :null => false
|
||||
end
|
||||
add_index :tags, :name, :unique => true
|
||||
|
||||
create_table :taggings do |t|
|
||||
t.column :<%= parent_association_name -%>_id, :integer, :null => false
|
||||
t.column :taggable_id, :integer, :null => false
|
||||
t.column :taggable_type, :string, :null => false
|
||||
# t.column :position, :integer
|
||||
end
|
||||
add_index :taggings, [:<%= parent_association_name -%>_id, :taggable_id, :taggable_type], :unique => true
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :tags
|
||||
drop_table :taggings
|
||||
end
|
||||
|
||||
end
|
||||
35
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag.rb
vendored
Normal file
35
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag.rb
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
class Tag < ActiveRecord::Base
|
||||
|
||||
DELIMITER = " " # how to separate tags in strings (you may
|
||||
# also need to change the validates_format_of parameters
|
||||
# if you update this)
|
||||
|
||||
# if speed becomes an issue, you could remove these validations
|
||||
# and rescue the AR index errors instead
|
||||
validates_presence_of :name
|
||||
validates_uniqueness_of :name, :case_sensitive => false
|
||||
validates_format_of :name, :with => /^[a-zA-Z0-9\_\-]+$/,
|
||||
:message => "can not contain special characters"
|
||||
|
||||
has_many_polymorphs :taggables,
|
||||
:from => [<%= taggable_models.join(", ") %>],
|
||||
:through => :taggings,
|
||||
:dependent => :destroy,
|
||||
<% if options[:self_referential] -%> :as => :<%= parent_association_name -%>,
|
||||
<% end -%>
|
||||
:skip_duplicates => false,
|
||||
:parent_extend => proc { # XXX this isn't right
|
||||
def to_s
|
||||
self.map(&:name).sort.join(Tag::DELIMITER)
|
||||
end
|
||||
}
|
||||
|
||||
def before_create
|
||||
# if you allow editable tag names, you might want before_save instead
|
||||
self.name = name.downcase.strip.squeeze(" ")
|
||||
end
|
||||
|
||||
class Error < StandardError
|
||||
end
|
||||
|
||||
end
|
||||
10
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag_test.rb
vendored
Normal file
10
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag_test.rb
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class TagTest < Test::Unit::TestCase
|
||||
fixtures :tags, :taggings, :recipes, :posts
|
||||
|
||||
def test_to_s
|
||||
assert_equal "delicious sexy", Recipe.find(2).tags.to_s
|
||||
end
|
||||
|
||||
end
|
||||
15
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging.rb
vendored
Normal file
15
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging.rb
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
class Tagging < ActiveRecord::Base
|
||||
|
||||
belongs_to :<%= parent_association_name -%><%= ", :foreign_key => \"#{parent_association_name}_id\", :class_name => \"Tag\"" if options[:self_referential] %>
|
||||
belongs_to :taggable, :polymorphic => true
|
||||
|
||||
# if you want acts_as_list, you will have to manage the tagging positions
|
||||
# manually, by created decorated join records
|
||||
# acts_as_list :scope => :taggable
|
||||
|
||||
def before_destroy
|
||||
# if all the taggings for a particular <%= parent_association_name -%> are deleted, we want to
|
||||
# delete the <%= parent_association_name -%> too
|
||||
<%= parent_association_name -%>.destroy_without_callbacks if <%= parent_association_name -%>.taggings.count == 1
|
||||
end
|
||||
end
|
||||
88
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_extensions.rb
vendored
Normal file
88
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_extensions.rb
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
class ActiveRecord::Base
|
||||
|
||||
# the alternative to these taggable?() checks is to explicitly include a
|
||||
# TaggingMethods module (which you would create) in each taggable model
|
||||
|
||||
def tag_with list
|
||||
# completely replace the existing tag set
|
||||
taggable?(true)
|
||||
list = tag_cast_to_string(list)
|
||||
|
||||
Tag.transaction do # transactions may not be ideal for you here
|
||||
current = <%= parent_association_name -%>s.map(&:name)
|
||||
_add_tags(list - current)
|
||||
_remove_tags(current - list)
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
alias :<%= parent_association_name -%>s= :tag_with
|
||||
|
||||
# need to avoid name conflicts with the built-in ActiveRecord association
|
||||
# methods, thus the underscores
|
||||
def _add_tags incoming
|
||||
taggable?(true)
|
||||
tag_cast_to_string(incoming).each do |tag_name|
|
||||
begin
|
||||
tag = Tag.find_or_create_by_name(tag_name)
|
||||
raise Tag::Error, "tag could not be saved: #{tag_name}" if tag.new_record?
|
||||
tag.taggables << self
|
||||
rescue ActiveRecord::StatementInvalid => e
|
||||
raise unless e.to_s =~ /duplicate/i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def _remove_tags outgoing
|
||||
taggable?(true)
|
||||
outgoing = tag_cast_to_string(outgoing)
|
||||
<% if options[:self_referential] %>
|
||||
# because of http://dev.rubyonrails.org/ticket/6466
|
||||
taggings.destroy(taggings.find(:all, :include => :<%= parent_association_name -%>).select do |tagging|
|
||||
outgoing.include? tagging.<%= parent_association_name -%>.name
|
||||
end)
|
||||
<% else -%>
|
||||
<%= parent_association_name -%>s.delete(<%= parent_association_name -%>s.select do |tag|
|
||||
outgoing.include? tag.name
|
||||
end)
|
||||
<% end -%>
|
||||
end
|
||||
|
||||
def tag_list
|
||||
taggable?(true)
|
||||
<%= parent_association_name -%>s.reload
|
||||
<%= parent_association_name -%>s.to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tag_cast_to_string obj
|
||||
case obj
|
||||
when Array
|
||||
obj.map! do |item|
|
||||
case item
|
||||
when /^\d+$/, Fixnum then Tag.find(item).name # this will be slow if you use ids a lot
|
||||
when Tag then item.name
|
||||
when String then item
|
||||
else
|
||||
raise "Invalid type"
|
||||
end
|
||||
end
|
||||
when String
|
||||
obj = obj.split(Tag::DELIMITER).map do |tag_name|
|
||||
tag_name.strip.squeeze(" ")
|
||||
end
|
||||
else
|
||||
raise "Invalid object of class #{obj.class} as tagging method parameter"
|
||||
end.flatten.compact.map(&:downcase).uniq
|
||||
end
|
||||
|
||||
def taggable?(should_raise = false)
|
||||
unless flag = respond_to?(:<%= parent_association_name -%>s)
|
||||
raise "#{self.class} is not a taggable model" if should_raise
|
||||
end
|
||||
flag
|
||||
end
|
||||
|
||||
end
|
||||
58
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_test.rb
vendored
Normal file
58
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_test.rb
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
class TaggingTest < Test::Unit::TestCase
|
||||
fixtures :taggings, :tags, :posts, :recipes
|
||||
|
||||
def setup
|
||||
@obj1 = Recipe.find(1)
|
||||
@obj2 = Recipe.find(2)
|
||||
@obj3 = Post.find(1)
|
||||
@tag1 = Tag.find(1)
|
||||
@tag2 = Tag.find(2)
|
||||
@tagging1 = Tagging.find(1)
|
||||
end
|
||||
|
||||
def test_tag_with
|
||||
@obj2.tag_with "dark columbian"
|
||||
assert_equal "columbian dark", @obj2.tag_list
|
||||
end
|
||||
|
||||
<% if options[:self_referential] -%>
|
||||
def test_self_referential_tag_with
|
||||
@tag1.tag_with [1, 2]
|
||||
assert @tag1.tags.include?(@tag1)
|
||||
assert !@tag2.tags.include?(@tag1)
|
||||
end
|
||||
|
||||
<% end -%>
|
||||
def test__add_tags
|
||||
@obj1._add_tags "porter longneck"
|
||||
assert Tag.find_by_name("porter").taggables.include?(@obj1)
|
||||
assert Tag.find_by_name("longneck").taggables.include?(@obj1)
|
||||
assert_equal "delicious longneck porter", @obj1.tag_list
|
||||
|
||||
@obj1._add_tags [2]
|
||||
assert_equal "delicious longneck porter sexy", @obj1.tag_list
|
||||
end
|
||||
|
||||
def test__remove_tags
|
||||
@obj2._remove_tags ["2", @tag1]
|
||||
assert @obj2.tags.empty?
|
||||
end
|
||||
|
||||
def test_tag_list
|
||||
assert_equal "delicious sexy", @obj2.tag_list
|
||||
end
|
||||
|
||||
def test_taggable
|
||||
assert_raises(RuntimeError) do
|
||||
@tagging1.send(:taggable?, true)
|
||||
end
|
||||
assert !@tagging1.send(:taggable?)
|
||||
assert @obj3.send(:taggable?)
|
||||
<% if options[:self_referential] -%>
|
||||
assert @tag1.send(:taggable?)
|
||||
<% end -%>
|
||||
end
|
||||
|
||||
end
|
||||
21
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/taggings.yml
vendored
Normal file
21
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/taggings.yml
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
taggings_003:
|
||||
<%= parent_association_name -%>_id: "2"
|
||||
id: "3"
|
||||
taggable_type: <%= taggable_models[0][1..-1].classify %>
|
||||
taggable_id: "1"
|
||||
taggings_004:
|
||||
<%= parent_association_name -%>_id: "2"
|
||||
id: "4"
|
||||
taggable_type: <%= taggable_models[1][1..-1].classify %>
|
||||
taggable_id: "2"
|
||||
taggings_001:
|
||||
<%= parent_association_name -%>_id: "1"
|
||||
id: "1"
|
||||
taggable_type: <%= taggable_models[1][1..-1].classify %>
|
||||
taggable_id: "1"
|
||||
taggings_002:
|
||||
<%= parent_association_name -%>_id: "1"
|
||||
id: "2"
|
||||
taggable_type: <%= taggable_models[1][1..-1].classify %>
|
||||
taggable_id: "2"
|
||||
7
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tags.yml
vendored
Normal file
7
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tags.yml
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
tags_001:
|
||||
name: delicious
|
||||
id: "1"
|
||||
tags_002:
|
||||
name: sexy
|
||||
id: "2"
|
||||
0
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tags_controller.rb
vendored
Normal file
0
tracks/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tags_controller.rb
vendored
Normal file
|
|
@ -1 +1,2 @@
|
|||
|
||||
require 'has_many_polymorphs'
|
||||
|
|
|
|||
1
tracks/vendor/plugins/has_many_polymorphs/install.rb
vendored
Normal file
1
tracks/vendor/plugins/has_many_polymorphs/install.rb
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
puts open("#{File.dirname(__FILE__)}/TAGGING_INSTALL").read
|
||||
|
|
@ -1,581 +1,31 @@
|
|||
|
||||
# self-referential, polymorphic has_many :through plugin
|
||||
# http://blog.evanweaver.com/articles/2006/06/02/has_many_polymorphs
|
||||
# http://blog.evanweaver.com/pages/has_many_polymorphs
|
||||
# operates via magic dust, and courage
|
||||
|
||||
if defined? Rails::Configuration
|
||||
class Rails::Configuration
|
||||
def has_many_polymorphs_cache_classes= *args
|
||||
::ActiveRecord::Associations::ClassMethods.has_many_polymorphs_cache_classes = *args
|
||||
end
|
||||
end
|
||||
require 'active_record'
|
||||
|
||||
require 'has_many_polymorphs/reflection'
|
||||
require 'has_many_polymorphs/association'
|
||||
require 'has_many_polymorphs/class_methods'
|
||||
|
||||
require 'has_many_polymorphs/support_methods'
|
||||
require 'has_many_polymorphs/configuration'
|
||||
require 'has_many_polymorphs/base'
|
||||
|
||||
class ActiveRecord::Base
|
||||
extend ActiveRecord::Associations::PolymorphicClassMethods
|
||||
end
|
||||
|
||||
module ActiveRecord
|
||||
|
||||
if ENV['RAILS_ENV'] =~ /development|test/ and ENV['USER'] == 'eweaver'
|
||||
# enable this condition to get awesome association debugging
|
||||
# you will get a folder "generated_models" in the current dir containing valid Ruby files
|
||||
# explaining all ActiveRecord relationships set up by the plugin, as well as listing the
|
||||
# line in the plugin that made each particular macro call
|
||||
class << Base
|
||||
COLLECTION_METHODS = [:belongs_to, :has_many, :has_and_belongs_to_many, :has_one].each do |method_name|
|
||||
alias_method "original_#{method_name}".to_sym, method_name
|
||||
undef_method method_name
|
||||
end
|
||||
|
||||
unless defined? GENERATED_CODE_DIR
|
||||
# automatic code generation for debugging... bitches
|
||||
GENERATED_CODE_DIR = "generated_models"
|
||||
system "rm -rf #{GENERATED_CODE_DIR}"
|
||||
Dir.mkdir GENERATED_CODE_DIR
|
||||
|
||||
alias :original_method_missing :method_missing
|
||||
def method_missing(method_name, *args, &block)
|
||||
if COLLECTION_METHODS.include? method_name.to_sym
|
||||
Dir.chdir GENERATED_CODE_DIR do
|
||||
filename = "#{ActiveRecord::Associations::ClassMethods.demodulate(self.name.underscore)}.rb"
|
||||
contents = File.open(filename).read rescue "\nclass #{self.name}\n\nend\n"
|
||||
line = caller[1][/\:(\d+)\:/, 1]
|
||||
contents[-5..-5] = "\n #{method_name} #{args[0..-2].inspect[1..-2]},\n #{args[-1].inspect[1..-2].gsub(" :", "\n :").gsub("=>", " => ")}\n#{ block ? " #{block.inspect.sub(/\@.*\//, '@')}\n" : ""} # called from line #{line}\n\n"
|
||||
File.open(filename, "w") do |file|
|
||||
file.puts contents
|
||||
end
|
||||
end
|
||||
# doesn't handle blocks
|
||||
self.send("original_#{method_name}", *args, &block)
|
||||
else
|
||||
self.send(:original_method_missing, method_name, *args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# and we want to track the reloader's shenanigans
|
||||
(::Dependencies.log_activity = true) rescue nil
|
||||
end
|
||||
|
||||
module Associations
|
||||
module ClassMethods
|
||||
mattr_accessor :has_many_polymorphs_cache_classes
|
||||
|
||||
def acts_as_double_polymorphic_join opts
|
||||
raise RuntimeError, "Couldn't understand #{opts.inspect} options in acts_as_double_polymorphic_join. Please only specify the two relationships and their member classes; there are no options to set. " unless opts.length == 2
|
||||
|
||||
join_name = self.name.tableize.to_sym
|
||||
opts.each do |polymorphs, children|
|
||||
parent_hash_key = (opts.keys - [polymorphs]).first # parents are the entries in the _other_ children array
|
||||
|
||||
begin
|
||||
parent_foreign_key = self.reflect_on_association(parent_hash_key.to_s.singularize.to_sym).primary_key_name
|
||||
rescue NoMethodError
|
||||
raise RuntimeError, "Couldn't find 'belongs_to' association for :#{parent_hash_key.to_s.singularize} in #{self.name}." unless parent_foreign_key
|
||||
end
|
||||
|
||||
parents = opts[parent_hash_key]
|
||||
conflicts = (children & parents) # set intersection
|
||||
parents.each do |parent_name|
|
||||
|
||||
parent_class = parent_name.to_s.classify.constantize
|
||||
reverse_polymorph = parent_hash_key.to_s.singularize
|
||||
polymorph = polymorphs.to_s.singularize
|
||||
|
||||
parent_class.send(:has_many_polymorphs,
|
||||
polymorphs, {:double => true,
|
||||
:from => children,
|
||||
:as => parent_hash_key.to_s.singularize.to_sym,
|
||||
:through => join_name,
|
||||
:dependent => :destroy,
|
||||
:foreign_key => parent_foreign_key,
|
||||
:foreign_type_key => parent_foreign_key.to_s.sub(/_id$/, '_type'),
|
||||
:reverse_polymorph => reverse_polymorph,
|
||||
:conflicts => conflicts,
|
||||
:rename_individual_collections => false})
|
||||
|
||||
if conflicts.include? parent_name
|
||||
# unify the alternate sides of the conflicting children
|
||||
(conflicts).each do |method_name|
|
||||
unless parent_class.instance_methods.include?(method_name)
|
||||
parent_class.send(:define_method, method_name) do
|
||||
(self.send("#{reverse_polymorph}_#{method_name}") +
|
||||
self.send("#{polymorph}_#{method_name}")).freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# unify the join model
|
||||
unless parent_class.instance_methods.include?(join_name)
|
||||
parent_class.send(:define_method, join_name) do
|
||||
(self.send("#{join_name}_as_#{reverse_polymorph}") +
|
||||
self.send("#{join_name}_as_#{polymorph}")).freeze
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def has_many_polymorphs(polymorphs, options, &block)
|
||||
options.assert_valid_keys(:from, :acts_as, :as, :through, :foreign_key, :dependent, :double,
|
||||
:rename_individual_collections, :foreign_type_key, :reverse_polymorph, :conflicts)
|
||||
|
||||
# the way this deals with extra parameters to the associations could use some work
|
||||
options[:as] ||= options[:acts_as] ||= self.table_name.singularize.to_sym
|
||||
|
||||
# foreign keys follow the table name, not the class name in Rails 2.0
|
||||
options[:foreign_key] ||= "#{options[:as].to_s}_id"
|
||||
|
||||
# no conflicts by default
|
||||
options[:conflicts] ||= []
|
||||
|
||||
# construct the join table name
|
||||
options[:through] ||= join_table((options[:as].to_s.pluralize or self.table_name), polymorphs)
|
||||
if options[:reverse_polymorph]
|
||||
options[:through_with_reverse_polymorph] = "#{options[:through]}_as_#{options[:reverse_polymorph]}".to_sym
|
||||
else
|
||||
options[:through_with_reverse_polymorph] = options[:through]
|
||||
end
|
||||
|
||||
options[:join_class_name] ||= options[:through].to_s.classify
|
||||
|
||||
# the class must have_many on the join_table
|
||||
opts = {:foreign_key => options[:foreign_key], :dependent => options[:dependent],
|
||||
:class_name => options[:join_class_name]}
|
||||
if options[:foreign_type_key]
|
||||
opts[:conditions] = "#{options[:foreign_type_key]} = #{quote_value self.base_class.name}"
|
||||
end
|
||||
|
||||
has_many demodulate(options[:through_with_reverse_polymorph]), opts
|
||||
|
||||
polymorph = polymorphs.to_s.singularize.to_sym
|
||||
|
||||
# add the base_class method to the join_table so that STI will work transparently
|
||||
inject_before_save_into_join_table(options[:join_class_name], polymorph)
|
||||
|
||||
# get some reusable info
|
||||
children, child_associations = {}, {}
|
||||
options[:from].each do |child_plural|
|
||||
children[child_plural] = child_plural.to_s.singularize.to_sym
|
||||
child_associations[child_plural] = (options[:rename_individual_collections] ? "#{polymorph}_#{child_plural}".to_sym : child_plural)
|
||||
end
|
||||
|
||||
# get our models out of the reloadable lists, if requested
|
||||
if self.has_many_polymorphs_cache_classes
|
||||
klasses = [self.name, options[:join_class_name], *children.values.map{|x| x.to_s.classify}]
|
||||
klasses += basify_sti_classnames(klasses).keys.to_a.compact.uniq.map{|x| x.to_s.classify}
|
||||
klasses.uniq!
|
||||
klasses.each {|s| logger.debug "Ejecting #{s.inspect} from the autoload lists"}
|
||||
begin
|
||||
Dependencies.autoloaded_constants -= klasses
|
||||
Dependencies.explicitly_unloadable_constants -= klasses
|
||||
rescue NoMethodError
|
||||
raise "Rails 1.2.0 or later is required to set config.has_many_polymorphs_cache_classes = true"
|
||||
end
|
||||
end
|
||||
|
||||
# auto-inject individually named associations for the children into the join model
|
||||
create_virtual_associations_for_join_to_individual_children(children, polymorph, options)
|
||||
|
||||
# iterate through the polymorphic children, running the parent class's :has_many on each one
|
||||
create_has_many_through_associations_for_parent_to_children(children, child_associations, polymorphs, polymorph, options)
|
||||
|
||||
# auto-inject the regular polymorphic associations into the child classes
|
||||
create_has_many_through_associations_for_children_to_parent(children, polymorph, options)
|
||||
|
||||
create_general_collection_association_for_parent(polymorphs, polymorph, basify_sti_classnames(children), options, &block)
|
||||
end
|
||||
|
||||
def self.demodulate(s)
|
||||
s.to_s.gsub('/', '_').to_sym
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def demodulate(s)
|
||||
ActiveRecord::Associations::ClassMethods.demodulate(s)
|
||||
end
|
||||
|
||||
def basify_sti_classnames(hash)
|
||||
# this blows
|
||||
result = {}
|
||||
hash.each do |plural, singular|
|
||||
klass = plural.to_s.classify.constantize
|
||||
if klass != klass.base_class
|
||||
result[klass.base_class.table_name.to_sym] = klass.base_class.table_name.singularize.to_sym
|
||||
else
|
||||
result[plural] = singular
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def inject_before_save_into_join_table(join_class_name, polymorph)
|
||||
sti_hook = "sti_class_rewrite"
|
||||
rewrite_procedure = %[
|
||||
self.send(:#{polymorph}_type=, self.#{polymorph}_type.constantize.base_class.name)
|
||||
]
|
||||
|
||||
# this also blows, and should be abstracted. alias_method_chain is not enough.
|
||||
join_class_name.constantize.class_eval %[
|
||||
unless instance_methods.include? "before_save_with_#{sti_hook}"
|
||||
if instance_methods.include? "before_save"
|
||||
alias_method :before_save_without_#{sti_hook}, :before_save
|
||||
def before_save_with_#{sti_hook}
|
||||
before_save_without_#{sti_hook}
|
||||
#{rewrite_procedure}
|
||||
end
|
||||
else
|
||||
def before_save_with_#{sti_hook}
|
||||
#{rewrite_procedure}
|
||||
end
|
||||
end
|
||||
alias_method :before_save, :before_save_with_#{sti_hook}
|
||||
end
|
||||
]
|
||||
|
||||
end
|
||||
|
||||
def create_virtual_associations_for_join_to_individual_children(children, polymorph, options)
|
||||
children.each do |child_plural, child|
|
||||
options[:join_class_name].constantize.instance_eval do
|
||||
|
||||
association_name = child.to_s
|
||||
association_name += "_as_#{polymorph}" if options[:conflicts].include?(child_plural)
|
||||
association = demodulate(association_name)
|
||||
|
||||
opts = {:class_name => child.to_s.classify,
|
||||
:foreign_key => "#{polymorph}_id" }
|
||||
|
||||
unless self.reflect_on_all_associations.map(&:name).include? association
|
||||
belongs_to association, opts
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_has_many_through_associations_for_children_to_parent(children, polymorph, options)
|
||||
children.each do |child_plural, child|
|
||||
|
||||
if child == options[:as]
|
||||
raise RuntimeError, "You can't have a self-referential polymorphic has_many :through without renaming the non-polymorphic foreign key in the join model."
|
||||
end
|
||||
|
||||
parent = self
|
||||
child.to_s.classify.constantize.instance_eval do
|
||||
|
||||
# this shouldn't be called at all during doubles; there is no way to traverse to a
|
||||
# double polymorphic parent (XXX is that right?)
|
||||
unless options[:double] or options[:conflicts].include? self.name.tableize.to_sym
|
||||
begin
|
||||
require_dependency parent.name.underscore # XXX why is this here?
|
||||
rescue MissingSourceFile
|
||||
end
|
||||
|
||||
# the join table
|
||||
through = demodulate(options[:through_with_reverse_polymorph]).to_s
|
||||
through += "_as_child" if parent == self
|
||||
through = through.to_sym
|
||||
|
||||
has_many through, :as => polymorph,
|
||||
:class_name => options[:through].to_s.classify,
|
||||
:dependent => options[:dependent]
|
||||
|
||||
association = options[:as].to_s.pluralize
|
||||
association += "_of_#{polymorph.to_s.pluralize}" if options[:rename_individual_collections] # XXX check this
|
||||
|
||||
# the polymorphic parent association
|
||||
has_many association.to_sym, :through => through,
|
||||
:class_name => parent.name,
|
||||
:source => options[:as],
|
||||
:foreign_key => options[:foreign_key]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_has_many_through_associations_for_parent_to_children(children, child_associations, polymorphs, polymorph, options)
|
||||
children.each do |child_plural, child|
|
||||
#puts ":source => #{child}"
|
||||
association = demodulate(child_associations[child_plural]).to_s
|
||||
source = demodulate(child).to_s
|
||||
|
||||
if options[:conflicts].include? child_plural
|
||||
# XXX what?
|
||||
association = "#{polymorph}_#{association}" if options[:conflicts].include? self.name.tableize.to_sym
|
||||
source += "_as_#{polymorph}"
|
||||
end
|
||||
|
||||
# activerecord is broken when you try to anonymously extend an association in a namespaced model,
|
||||
extension = self.class_eval %[
|
||||
module #{association.classify + "AssociationExtension"}
|
||||
def push *args
|
||||
proxy_owner.send(:#{polymorphs}).send(:push, *args).select{|x| x.is_a? #{child.to_s.classify}}
|
||||
end
|
||||
alias :<< :push
|
||||
def delete *args
|
||||
proxy_owner.send(:#{polymorphs}).send(:delete, *args)
|
||||
end
|
||||
def clear
|
||||
proxy_owner.send(:#{polymorphs}).send(:clear, #{child.to_s.classify})
|
||||
end
|
||||
self # required
|
||||
end]
|
||||
|
||||
has_many association.to_sym, :through => demodulate(options[:through_with_reverse_polymorph]),
|
||||
:source => source.to_sym,
|
||||
:conditions => ["#{options[:join_class_name].constantize.table_name}.#{polymorph}_type = ?", child.to_s.classify.constantize.base_class.name],
|
||||
:extend => extension
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def create_general_collection_association_for_parent(collection_name, polymorph, children, options, &block)
|
||||
# we need to explicitly rename all the columns because we are fetching all the children objects at once.
|
||||
# if multiple objects have a 'title' column, for instance, there will be a collision and we will potentially
|
||||
# lose data. if we alias the fields and then break them up later, there are no collisions.
|
||||
join_model = options[:through].to_s.classify.constantize
|
||||
|
||||
# figure out what fields we wanna grab
|
||||
select_fields = []
|
||||
children.each do |plural, singular|
|
||||
klass = plural.to_s.classify.constantize
|
||||
klass.columns.map(&:name).each do |name|
|
||||
select_fields << "#{klass.table_name}.#{name} as #{demodulate plural}_#{name}"
|
||||
end
|
||||
end
|
||||
|
||||
# now get the join model fields
|
||||
join_model.columns.map(&:name).each do |name|
|
||||
select_fields << "#{join_model.table_name}.#{name} as #{join_model.table_name}_#{name}"
|
||||
end
|
||||
|
||||
from_table = self.table_name
|
||||
left_joins = children.keys.map do |n|
|
||||
klass = n.to_s.classify.constantize
|
||||
"LEFT JOIN #{klass.table_name} ON #{join_model.table_name}.#{polymorph}_id = #{klass.table_name}.#{klass.primary_key} AND #{join_model.table_name}.#{polymorph}_type = '#{n.to_s.classify}'"
|
||||
end
|
||||
|
||||
sql_query = 'SELECT ' + select_fields.join(', ') + " FROM #{join_model.table_name}" +
|
||||
"\nJOIN #{from_table} as polymorphic_parent ON #{join_model.table_name}.#{options[:foreign_key]} = polymorphic_parent.#{self.primary_key}\n" +
|
||||
left_joins.join("\n") + "\nWHERE "
|
||||
|
||||
if options[:foreign_type_key]
|
||||
sql_query +="#{join_model.table_name}.#{options[:foreign_type_key]} = #{quote_value self.base_class.name} AND "
|
||||
end
|
||||
|
||||
# for sqlite3 you have to reference the left-most table in WHERE clauses or rows with NULL
|
||||
# join results sometimes get silently dropped. it's stupid.
|
||||
sql_query += "#{join_model.table_name}.#{options[:foreign_key]} "
|
||||
#puts("Built collection property query:\n #{sql_query}")
|
||||
|
||||
class_eval do
|
||||
attr_accessor "#{collection_name}_cache"
|
||||
cattr_accessor "#{collection_name}_options"
|
||||
|
||||
define_method(collection_name) do
|
||||
if collection_name_cache = instance_variable_get("@#{collection_name}_cache")
|
||||
#puts("Cache hit on #{collection_name}")
|
||||
collection_name_cache
|
||||
else
|
||||
#puts("Cache miss on #{collection_name}")
|
||||
rows = connection.select_all("#{sql_query}" + (new_record? ? "IS NULL" : "= #{self.id}"))
|
||||
# this gives us a hash with keys for each object type
|
||||
objectified = objectify_polymorphic_array(rows, "#{join_model}", "#{polymorph}_type")
|
||||
# locally cache the different object types found
|
||||
# this doesn't work... yet.
|
||||
objectified.each do |key, array|
|
||||
instance_variable_set("@#{ActiveRecord::Associations::ClassMethods.demodulate(key)}", array)
|
||||
end
|
||||
proxy_object = HasManyPolymorphsProxyCollection.new(objectified[:all], self, send("#{collection_name}_options"))
|
||||
(class << proxy_object; self end).send(:class_eval, &block) if block_given?
|
||||
instance_variable_set("@#{collection_name}_cache", proxy_object)
|
||||
end
|
||||
end
|
||||
|
||||
# in order not to break tests, see if we have been defined already
|
||||
unless instance_methods.include? "reload_with_#{collection_name}"
|
||||
define_method("reload_with_#{collection_name}") do
|
||||
send("reload_without_#{collection_name}")
|
||||
instance_variable_set("@#{collection_name}_cache", nil)
|
||||
self
|
||||
end
|
||||
|
||||
alias_method "reload_without_#{collection_name}", :reload
|
||||
alias_method :reload, "reload_with_#{collection_name}"
|
||||
end
|
||||
end
|
||||
|
||||
send("#{collection_name}_options=",
|
||||
options.merge(:collection_name => collection_name,
|
||||
:type_key => "#{polymorph}_type",
|
||||
:id_key => "#{polymorph}_id"))
|
||||
|
||||
# puts("Defined the collection proxy.\n#{collection_name}\n")
|
||||
end
|
||||
|
||||
def join_table(a, b)
|
||||
[a.to_s, b.to_s].sort.join("_").to_sym
|
||||
end
|
||||
|
||||
unless self.respond_to? :quote_value
|
||||
# hack it in (very badly) for Rails 1.1.6 people
|
||||
def quote_value s
|
||||
"'#{s.inspect[1..-2]}'"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
################################################
|
||||
|
||||
# decided to leave this alone unless it becomes clear that there is some benefit
|
||||
# in deriving from AssociationProxy
|
||||
#
|
||||
# the benefit would be custom finders on the collection, perhaps...
|
||||
class HasManyPolymorphsProxyCollection < Array
|
||||
|
||||
alias :array_delete :delete
|
||||
alias :array_push :push
|
||||
alias :count :length
|
||||
|
||||
def initialize(contents, parent, options)
|
||||
@parent = parent
|
||||
@options = options
|
||||
@join_class = options[:join_class_name].constantize
|
||||
return if contents.blank?
|
||||
super(contents)
|
||||
end
|
||||
|
||||
def push(objs, args={})
|
||||
objs = [objs] unless objs.is_a? Array
|
||||
|
||||
objs.each do |obj|
|
||||
data = {@options[:foreign_key] => @parent.id,
|
||||
@options[:type_key] => obj.class.base_class.to_s, @options[:id_key] => obj.id}
|
||||
data.merge!({@options[:foreign_type_key] => @parent.class.base_class.to_s}) if @options[:foreign_type_key] # for double polymorphs
|
||||
conditions_string = data.keys.map(&:to_s).push("").join(" = ? AND ")[0..-6]
|
||||
if @join_class.find(:first, :conditions => [conditions_string] + data.values).blank?
|
||||
@join_class.new(data).save!
|
||||
end
|
||||
end
|
||||
|
||||
if args[:reload]
|
||||
reload
|
||||
else
|
||||
# we have to do this funky stuff instead of just array difference because +/.uniq returns a regular array,
|
||||
# which doesn't have our special methods and configuration anymore
|
||||
unless (difference = objs - collection).blank?
|
||||
@parent.send("#{@options[:collection_name]}_cache=".to_sym, collection.array_push(*difference))
|
||||
end
|
||||
end
|
||||
|
||||
@parent.send(@options[:collection_name])
|
||||
end
|
||||
|
||||
alias :<< :push
|
||||
|
||||
def delete(objs, args={})
|
||||
|
||||
if objs
|
||||
objs = [objs] unless objs.is_a? Array
|
||||
elsif args[:clear]
|
||||
objs = collection
|
||||
objs = objs.select{|obj| obj.is_a? args[:klass]} if args[:klass]
|
||||
else
|
||||
raise RuntimeError, "Invalid delete parameters (has_many_polymorphs)."
|
||||
end
|
||||
|
||||
records = []
|
||||
objs.each do |obj|
|
||||
records += join_records.select do |record|
|
||||
record.send(@options[:type_key]) == obj.class.base_class.to_s and
|
||||
record.send(@options[:id_key]) == obj.id
|
||||
end
|
||||
end
|
||||
|
||||
reload if args[:reload]
|
||||
unless records.blank?
|
||||
records.map(&:destroy)
|
||||
# XXX could be faster if we reversed the loops
|
||||
deleted_items = collection.select do |item|
|
||||
records.select {|join_record|
|
||||
join_record.send(@options[:type_key]) == item.class.base_class.name and
|
||||
join_record.send(@options[:id_key]) == item.id
|
||||
}.length > 0
|
||||
end
|
||||
# keep the cache fresh, while we're at it. see comment in .push
|
||||
deleted_items.each { |item| collection.array_delete(item) }
|
||||
@parent.send("#{@options[:collection_name]}_cache=", collection)
|
||||
|
||||
return deleted_items unless deleted_items.empty?
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def clear(klass = nil)
|
||||
result = delete(nil, :clear => true, :klass => klass)
|
||||
return result if result
|
||||
collection
|
||||
end
|
||||
|
||||
def reload
|
||||
# reset the cache, postponing reloading from the db until we really need it
|
||||
@parent.reload
|
||||
end
|
||||
|
||||
private
|
||||
def join_records
|
||||
@parent.send(ActiveRecord::Associations::ClassMethods.demodulate(@options[:through]))
|
||||
end
|
||||
|
||||
def collection
|
||||
@parent.send(@options[:collection_name])
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Base
|
||||
# turns an array of hashes (db rows) into a hash consisting of :all (array of everything) and
|
||||
# a hash key for each class type it finds, e.g. :posts and :comments
|
||||
private
|
||||
def objectify_polymorphic_array(array, join_model, type_field)
|
||||
join_model = join_model.constantize
|
||||
arrays_hash = {}
|
||||
|
||||
array.each do |element|
|
||||
klass = element["#{join_model.table_name}_#{type_field}"].constantize
|
||||
association = ActiveRecord::Associations::ClassMethods.demodulate(klass.name.pluralize.underscore.downcase)
|
||||
hash = {}
|
||||
|
||||
# puts "Class #{klass.inspect}"
|
||||
# puts "Association name: #{association.inspect}"
|
||||
|
||||
element.each do |key, value|
|
||||
# puts "key #{key} - value #{value.inspect}"
|
||||
if key =~ /^#{association}_(.+)/
|
||||
hash[$1] = value
|
||||
# puts "#{$1.inspect} assigned #{value.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
object = klass.instantiate(hash)
|
||||
|
||||
arrays_hash[:all] ||= []
|
||||
arrays_hash[association] ||= []
|
||||
arrays_hash[:all] << object
|
||||
arrays_hash[association] << object
|
||||
end
|
||||
|
||||
arrays_hash
|
||||
end
|
||||
end
|
||||
if ENV['RAILS_ENV'] =~ /development|test/ and ENV['USER'] == 'eweaver'
|
||||
_logger_warn "has_many_polymorphs: debug mode enabled"
|
||||
require 'has_many_polymorphs/debugging_tools'
|
||||
end
|
||||
|
||||
#require 'ruby-debug'
|
||||
#Debugger.start
|
||||
if defined? Rails and RAILS_ENV and RAILS_ROOT
|
||||
_logger_warn "has_many_polymorphs: Rails environment detected"
|
||||
require 'has_many_polymorphs/dependencies'
|
||||
require 'has_many_polymorphs/autoload'
|
||||
end
|
||||
|
||||
_logger_debug "has_many_polymorphs: loaded ok"
|
||||
|
|
|
|||
153
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/association.rb
vendored
Normal file
153
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/association.rb
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
module ActiveRecord
|
||||
module Associations
|
||||
|
||||
class PolymorphicError < ActiveRecordError; end
|
||||
class PolymorphicMethodNotSupportedError < ActiveRecordError; end
|
||||
|
||||
class PolymorphicAssociation < HasManyThroughAssociation
|
||||
|
||||
def <<(*records)
|
||||
return if records.empty?
|
||||
|
||||
if @reflection.options[:skip_duplicates]
|
||||
_logger_debug "Loading instances for polymorphic duplicate push check; use :skip_duplicates => false and perhaps a database constraint to avoid this possible performance issue"
|
||||
load_target
|
||||
end
|
||||
|
||||
@reflection.klass.transaction do
|
||||
flatten_deeper(records).each do |record|
|
||||
if @owner.new_record? or not record.respond_to?(:new_record?) or record.new_record?
|
||||
raise PolymorphicError, "You can't associate unsaved records."
|
||||
end
|
||||
next if @reflection.options[:skip_duplicates] and @target.include? record
|
||||
@owner.send(@reflection.through_reflection.name).proxy_target << @reflection.klass.create!(construct_join_attributes(record))
|
||||
@target << record if loaded?
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
alias :push :<<
|
||||
alias :concat :<<
|
||||
|
||||
def find(*args)
|
||||
# super(*(args << returning(Base.send(:extract_options_from_args!, args)) {|opts| opts.delete :include})) # returning is slow
|
||||
opts = Base.send(:extract_options_from_args!, args)
|
||||
opts.delete :include
|
||||
super(*(args + [opts]))
|
||||
end
|
||||
|
||||
def construct_scope
|
||||
_logger_warn "Warning; not all usage scenarios for polymorphic scopes are supported yet."
|
||||
super
|
||||
end
|
||||
|
||||
def delete(*records)
|
||||
records = flatten_deeper(records)
|
||||
records.reject! {|record| @target.delete(record) if record.new_record?}
|
||||
return if records.empty?
|
||||
|
||||
@reflection.klass.transaction do
|
||||
records.each do |record|
|
||||
joins = @reflection.through_reflection.name
|
||||
@owner.send(joins).delete(@owner.send(joins).select do |join|
|
||||
join.send(@reflection.options[:polymorphic_key]) == record.id and
|
||||
join.send(@reflection.options[:polymorphic_type_key]) == "#{record.class.base_class}"
|
||||
end)
|
||||
@target.delete(record)
|
||||
end
|
||||
end
|
||||
# records
|
||||
end
|
||||
|
||||
def clear(klass = nil)
|
||||
load_target
|
||||
return if @target.empty?
|
||||
|
||||
if klass
|
||||
delete(@target.select {|r| r.is_a? klass })
|
||||
else
|
||||
@owner.send(@reflection.through_reflection.name).clear
|
||||
@target.clear
|
||||
end
|
||||
[]
|
||||
end
|
||||
|
||||
# undef :sum
|
||||
# undef :create!
|
||||
|
||||
protected
|
||||
|
||||
def construct_quoted_owner_attributes(*args)
|
||||
# no access to returning() here? why not?
|
||||
type_key = @reflection.options[:foreign_type_key]
|
||||
{@reflection.primary_key_name => @owner.id,
|
||||
type_key=> (@owner.class.base_class.name if type_key)}
|
||||
end
|
||||
|
||||
def construct_from
|
||||
# build the FROM part of the query, in this case, the polymorphic join table
|
||||
@reflection.klass.table_name
|
||||
end
|
||||
|
||||
def construct_owner
|
||||
# the table name for the owner object's class
|
||||
@owner.class.table_name
|
||||
end
|
||||
|
||||
def construct_owner_key
|
||||
# the primary key field for the owner object
|
||||
@owner.class.primary_key
|
||||
end
|
||||
|
||||
def construct_select(custom_select = nil)
|
||||
# build the select query
|
||||
selected = custom_select || @reflection.options[:select]
|
||||
end
|
||||
|
||||
def construct_joins(custom_joins = nil)
|
||||
# build the string of default joins
|
||||
"JOIN #{construct_owner} AS polymorphic_parent ON #{construct_from}.#{@reflection.options[:foreign_key]} = polymorphic_parent.#{construct_owner_key} " +
|
||||
@reflection.options[:from].map do |plural|
|
||||
klass = plural._as_class
|
||||
"LEFT JOIN #{klass.table_name} ON #{construct_from}.#{@reflection.options[:polymorphic_key]} = #{klass.table_name}.#{klass.primary_key} AND #{construct_from}.#{@reflection.options[:polymorphic_type_key]} = #{@reflection.klass.quote_value(klass.base_class.name)}"
|
||||
end.uniq.join(" ") + " #{custom_joins}"
|
||||
end
|
||||
|
||||
def construct_conditions
|
||||
# build the fully realized condition string
|
||||
conditions = construct_quoted_owner_attributes.map do |field, value|
|
||||
"#{construct_from}.#{field} = #{@reflection.klass.quote_value(value)}" if value
|
||||
end
|
||||
conditions << custom_conditions if custom_conditions
|
||||
"(" + conditions.compact.join(') AND (') + ")"
|
||||
end
|
||||
|
||||
def custom_conditions
|
||||
# custom conditions... not as messy as has_many :through because our joins are a little smarter
|
||||
if @reflection.options[:conditions]
|
||||
"(" + interpolate_sql(@reflection.klass.send(:sanitize_sql, @reflection.options[:conditions])) + ")"
|
||||
end
|
||||
end
|
||||
|
||||
alias :construct_owner_attributes :construct_quoted_owner_attributes
|
||||
alias :conditions :custom_conditions # XXX possibly not necessary
|
||||
alias :sql_conditions :custom_conditions # XXX ditto
|
||||
|
||||
# construct attributes for join for a particular record
|
||||
def construct_join_attributes(record)
|
||||
{@reflection.options[:polymorphic_key] => record.id,
|
||||
@reflection.options[:polymorphic_type_key] => "#{record.class.base_class}",
|
||||
@reflection.options[:foreign_key] => @owner.id}.merge(@reflection.options[:foreign_type_key] ?
|
||||
{@reflection.options[:foreign_type_key] => "#{@owner.class.base_class}"} : {}) # for double-sided relationships
|
||||
end
|
||||
|
||||
def build(attrs = nil)
|
||||
raise PolymorphicMethodNotSupportedError, "You can't associate new records."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
26
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/autoload.rb
vendored
Normal file
26
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/autoload.rb
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
require 'initializer'
|
||||
|
||||
class Rails::Initializer
|
||||
def after_initialize_with_autoload
|
||||
after_initialize_without_autoload
|
||||
|
||||
_logger_debug "has_many_polymorphs: autoload hook invoked"
|
||||
Dir["#{RAILS_ROOT}/app/models/**/*.rb"].each do |filename|
|
||||
next if filename =~ /svn|CVS|bzr/
|
||||
open filename do |file|
|
||||
if file.grep(/has_many_polymorphs|acts_as_double_polymorphic_join/).any?
|
||||
begin
|
||||
model = File.basename(filename)[0..-4].classify
|
||||
model.constantize
|
||||
_logger_warn "has_many_polymorphs: preloaded parent model #{model}"
|
||||
rescue Object => e
|
||||
_logger_warn "error preloading #{model}: #{e.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias_method_chain :after_initialize, :autoload
|
||||
end
|
||||
47
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/base.rb
vendored
Normal file
47
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/base.rb
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
module ActiveRecord
|
||||
class Base
|
||||
|
||||
class << self
|
||||
def instantiate_without_callbacks_with_polymorphic_checks(record)
|
||||
if record['polymorphic_parent_class']
|
||||
reflection = record['polymorphic_parent_class'].constantize.reflect_on_association(record['polymorphic_association_id'].to_sym)
|
||||
# _logger_debug "Instantiating a polymorphic row for #{record['polymorphic_parent_class']}.reflect_on_association(:#{record['polymorphic_association_id']})"
|
||||
|
||||
# rewrite 'record' with the right column names
|
||||
table_aliases = reflection.options[:table_aliases].dup
|
||||
record = Hash[*table_aliases.keys.map {|key| [key, record[table_aliases[key]]] }.flatten]
|
||||
|
||||
# find the real child class
|
||||
klass = record["#{self.table_name}.#{reflection.options[:polymorphic_type_key]}"].constantize
|
||||
if sti_klass = record["#{klass.table_name}.#{klass.inheritance_column}"]
|
||||
klass = klass.class_eval do compute_type(sti_klass) end # in case of namespaced STI models
|
||||
end
|
||||
|
||||
# check that the join actually joined to something
|
||||
unless (child_id = record["#{self.table_name}.#{reflection.options[:polymorphic_key]}"]) == record["#{klass.table_name}.#{klass.primary_key}"]
|
||||
raise ActiveRecord::Associations::PolymorphicError,
|
||||
"Referential integrity violation; child <#{klass.name}:#{child_id}> was not found for #{reflection.name.inspect}"
|
||||
end
|
||||
|
||||
# eject the join keys
|
||||
record = Hash[*record._select do |column, value|
|
||||
column[/^#{klass.table_name}/]
|
||||
end.map do |column, value|
|
||||
[column[/\.(.*)/, 1], value]
|
||||
end.flatten]
|
||||
|
||||
# allocate and assign values
|
||||
returning(klass.allocate) do |obj|
|
||||
obj.instance_variable_set("@attributes", record)
|
||||
end
|
||||
else
|
||||
instantiate_without_callbacks_without_polymorphic_checks(record)
|
||||
end
|
||||
end
|
||||
|
||||
alias_method_chain :instantiate_without_callbacks, :polymorphic_checks # oh yeah
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
452
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/class_methods.rb
vendored
Normal file
452
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/class_methods.rb
vendored
Normal file
|
|
@ -0,0 +1,452 @@
|
|||
|
||||
module ActiveRecord::Associations
|
||||
module PolymorphicClassMethods
|
||||
|
||||
#################
|
||||
# AR::Base association macros
|
||||
|
||||
RESERVED_KEYS = [:conditions, :order, :limit, :offset, :extend, :skip_duplicates,
|
||||
:join_extend, :dependent, :rename_individual_collections]
|
||||
|
||||
def acts_as_double_polymorphic_join options={}, &extension
|
||||
|
||||
collections = options._select {|k,v| v.is_a? Array and k.to_s !~ /(#{RESERVED_KEYS.map(&:to_s).join('|')})$/}
|
||||
raise PolymorphicError, "Couldn't understand options in acts_as_double_polymorphic_join. Valid parameters are your two class collections, and then #{RESERVED_KEYS.inspect[1..-2]}, with optionally your collection names prepended and joined with an underscore." unless collections.size == 2
|
||||
|
||||
options = options._select {|k,v| !collections[k]}
|
||||
options[:extend] = (options[:extend] ? Array(options[:extend]) + [extension] : extension) if extension # inline the block
|
||||
|
||||
collection_option_keys = Hash[*collections.keys.map do |key|
|
||||
[key, RESERVED_KEYS.map{|option| "#{key}_#{option}".to_sym}]
|
||||
end._flatten_once]
|
||||
|
||||
collections.keys.each do |collection|
|
||||
options.each do |key, value|
|
||||
next if collection_option_keys.values.flatten.include? key
|
||||
# shift the general options to the individual sides
|
||||
collection_value = options[collection_key = "#{collection}_#{key}".to_sym]
|
||||
case key
|
||||
when :conditions
|
||||
collection_value, value = sanitize_sql(collection_value), sanitize_sql(value)
|
||||
options[collection_key] = (collection_value ? "(#{collection_value}) AND (#{value})" : value)
|
||||
when :order
|
||||
options[collection_key] = (collection_value ? "#{collection_value}, #{value}" : value)
|
||||
when :extend, :join_extend
|
||||
options[collection_key] = Array(collection_value) + Array(value)
|
||||
when :limit, :offset, :dependent, :rename_individual_collections
|
||||
options[collection_key] ||= value
|
||||
else
|
||||
raise PolymorphicError, "Unknown option key #{key.inspect}."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
join_name = self.name.tableize.to_sym
|
||||
collections.each do |association_id, children|
|
||||
parent_hash_key = (collections.keys - [association_id]).first # parents are the entries in the _other_ children array
|
||||
|
||||
begin
|
||||
parent_foreign_key = self.reflect_on_association(parent_hash_key._singularize).primary_key_name
|
||||
rescue NoMethodError
|
||||
raise PolymorphicError, "Couldn't find 'belongs_to' association for :#{parent_hash_key._singularize} in #{self.name}." unless parent_foreign_key
|
||||
end
|
||||
|
||||
parents = collections[parent_hash_key]
|
||||
conflicts = (children & parents) # set intersection
|
||||
parents.each do |plural_parent_name|
|
||||
|
||||
parent_class = plural_parent_name._as_class
|
||||
singular_reverse_association_id = parent_hash_key._singularize
|
||||
|
||||
parent_class.send(:has_many_polymorphs,
|
||||
association_id, {:is_double => true,
|
||||
:from => children,
|
||||
:as => singular_reverse_association_id,
|
||||
:through => join_name.to_sym,
|
||||
:foreign_key => parent_foreign_key,
|
||||
:foreign_type_key => parent_foreign_key.to_s.sub(/_id$/, '_type'),
|
||||
:singular_reverse_association_id => singular_reverse_association_id,
|
||||
:conflicts => conflicts}.merge(Hash[*options._select do |key, value|
|
||||
collection_option_keys[association_id].include? key and !value.nil?
|
||||
end.map do |key, value|
|
||||
[key.to_s[association_id.to_s.length+1..-1].to_sym, value]
|
||||
end._flatten_once])) # rename side-specific options to general names
|
||||
|
||||
if conflicts.include? plural_parent_name
|
||||
# unify the alternate sides of the conflicting children
|
||||
(conflicts).each do |method_name|
|
||||
unless parent_class.instance_methods.include?(method_name)
|
||||
parent_class.send(:define_method, method_name) do
|
||||
(self.send("#{singular_reverse_association_id}_#{method_name}") +
|
||||
self.send("#{association_id._singularize}_#{method_name}")).freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# unify the join model... join model is always renamed for doubles, unlike child associations
|
||||
unless parent_class.instance_methods.include?(join_name)
|
||||
parent_class.send(:define_method, join_name) do
|
||||
(self.send("#{join_name}_as_#{singular_reverse_association_id}") +
|
||||
self.send("#{join_name}_as_#{association_id._singularize}")).freeze
|
||||
end
|
||||
end
|
||||
else
|
||||
unless parent_class.instance_methods.include?(join_name)
|
||||
parent_class.send(:alias_method, join_name, "#{join_name}_as_#{singular_reverse_association_id}")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def has_many_polymorphs (association_id, options = {}, &extension)
|
||||
_logger_debug "has_many_polymorphs: INIT"
|
||||
reflection = create_has_many_polymorphs_reflection(association_id, options, &extension)
|
||||
# puts "Created reflection #{reflection.inspect}"
|
||||
# configure_dependency_for_has_many(reflection)
|
||||
collection_reader_method(reflection, PolymorphicAssociation)
|
||||
end
|
||||
|
||||
def create_has_many_polymorphs_reflection(association_id, options, &extension)
|
||||
options.assert_valid_keys(
|
||||
:from,
|
||||
:as,
|
||||
:through,
|
||||
:foreign_key,
|
||||
:foreign_type_key,
|
||||
:polymorphic_key, # same as :association_foreign_key
|
||||
:polymorphic_type_key,
|
||||
:dependent, # default :destroy, only affects the join table
|
||||
:skip_duplicates, # default true, only affects the polymorphic collection
|
||||
:ignore_duplicates, # deprecated
|
||||
:is_double,
|
||||
:rename_individual_collections,
|
||||
:reverse_association_id, # not used
|
||||
:singular_reverse_association_id,
|
||||
:conflicts,
|
||||
:extend,
|
||||
:join_class_name,
|
||||
:join_extend,
|
||||
:parent_extend,
|
||||
:table_aliases,
|
||||
:select, # applies to the polymorphic relationship
|
||||
:conditions, # applies to the polymorphic relationship, the children, and the join
|
||||
# :include,
|
||||
:order, # applies to the polymorphic relationship, the children, and the join
|
||||
:group, # only applies to the polymorphic relationship and the children
|
||||
:limit, # only applies to the polymorphic relationship and the children
|
||||
:offset, # only applies to the polymorphic relationship
|
||||
:parent_order,
|
||||
:parent_group,
|
||||
:parent_limit,
|
||||
:parent_offset,
|
||||
# :source,
|
||||
:uniq, # XXX untested, only applies to the polymorphic relationship
|
||||
# :finder_sql,
|
||||
# :counter_sql,
|
||||
# :before_add,
|
||||
# :after_add,
|
||||
# :before_remove,
|
||||
# :after_remove
|
||||
:dummy)
|
||||
|
||||
# validate against the most frequent configuration mistakes
|
||||
verify_pluralization_of(association_id)
|
||||
raise PolymorphicError, ":from option must be an array" unless options[:from].is_a? Array
|
||||
options[:from].each{|plural| verify_pluralization_of(plural)}
|
||||
|
||||
options[:as] ||= self.table_name.singularize.to_sym
|
||||
options[:conflicts] = Array(options[:conflicts])
|
||||
options[:foreign_key] ||= "#{options[:as]}_id"
|
||||
|
||||
options[:association_foreign_key] =
|
||||
options[:polymorphic_key] ||= "#{association_id._singularize}_id"
|
||||
options[:polymorphic_type_key] ||= "#{association_id._singularize}_type"
|
||||
|
||||
if options.has_key? :ignore_duplicates
|
||||
_logger_warn "DEPRECATION WARNING: please use :skip_duplicates instead of :ignore_duplicates"
|
||||
options[:skip_duplicates] = options[:ignore_duplicates]
|
||||
end
|
||||
options[:skip_duplicates] = true unless options.has_key? :skip_duplicates
|
||||
options[:dependent] = :destroy unless options.has_key? :dependent
|
||||
options[:conditions] = sanitize_sql(options[:conditions])
|
||||
|
||||
# options[:finder_sql] ||= "(options[:polymorphic_key]
|
||||
|
||||
options[:through] ||= build_join_table_symbol((options[:as]._pluralize or self.table_name), association_id)
|
||||
options[:join_class_name] ||= options[:through]._classify
|
||||
options[:table_aliases] ||= build_table_aliases([options[:through]] + options[:from])
|
||||
options[:select] ||= build_select(association_id, options[:table_aliases])
|
||||
|
||||
options[:through] = "#{options[:through]}_as_#{options[:singular_reverse_association_id]}" if options[:singular_reverse_association_id]
|
||||
options[:through] = demodulate(options[:through]).to_sym
|
||||
|
||||
options[:extend] = spiked_create_extension_module(association_id, Array(options[:extend]) + Array(extension))
|
||||
options[:join_extend] = spiked_create_extension_module(association_id, Array(options[:join_extend]), "Join")
|
||||
options[:parent_extend] = spiked_create_extension_module(association_id, Array(options[:parent_extend]), "Parent")
|
||||
|
||||
# create the reflection object
|
||||
returning(create_reflection(:has_many_polymorphs, association_id, options, self)) do |reflection|
|
||||
if defined? Dependencies and RAILS_ENV == "development"
|
||||
_logger_warn "DEPRECATION WARNING: \"has_many_polymorphs_cache_classes =\" no longer has any effect. Please use \"config.cache_classes = true\" in the regular environment config (not in the \"after_initialize\" block)." if ActiveRecord::Associations::ClassMethods.has_many_polymorphs_cache_classes
|
||||
inject_dependencies(association_id, reflection) if Dependencies.mechanism == :load
|
||||
end
|
||||
|
||||
# set up the other related associations
|
||||
create_join_association(association_id, reflection)
|
||||
create_has_many_through_associations_for_parent_to_children(association_id, reflection)
|
||||
create_has_many_through_associations_for_children_to_parent(association_id, reflection)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
##############
|
||||
# table mapping for use at instantiation point
|
||||
|
||||
def build_table_aliases(from)
|
||||
# for the targets
|
||||
returning({}) do |aliases|
|
||||
from.map(&:to_s).sort.map(&:to_sym).each_with_index do |plural, t_index|
|
||||
table = plural._as_class.table_name
|
||||
plural._as_class.columns.map(&:name).each_with_index do |field, f_index|
|
||||
aliases["#{table}.#{field}"] = "t#{t_index}_r#{f_index}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def build_select(association_id, aliases)
|
||||
# cause instantiate has to know which reflection the results are coming from
|
||||
(["\'#{self.name}\' AS polymorphic_parent_class",
|
||||
"\'#{association_id}\' AS polymorphic_association_id"] +
|
||||
aliases.map do |table, _alias|
|
||||
"#{table} AS #{_alias}"
|
||||
end.sort).join(", ")
|
||||
end
|
||||
|
||||
##############
|
||||
# model caching
|
||||
|
||||
def inject_dependencies(association_id, reflection)
|
||||
_logger_debug "has_many_polymorphs: injecting dependencies"
|
||||
requirements = [self, reflection.klass].map{|klass| [klass, klass.base_class]}.flatten.uniq
|
||||
|
||||
# below, a contributed fix for a bug in doubles that I can't reproduce
|
||||
# parents = all_classes_for(association_id, reflection)
|
||||
# if (parents - requirements).empty?
|
||||
# requirements = (requirements - [parents[0]])
|
||||
# parents = [parents[0]]
|
||||
# else
|
||||
# parents = (parents - requirements)
|
||||
# end
|
||||
#
|
||||
# parents.each do |target_klass|
|
||||
# Dependencies.inject_dependency(target_klass, *requirements)
|
||||
# end
|
||||
|
||||
(all_classes_for(association_id, reflection) - requirements).each do |target_klass|
|
||||
Dependencies.inject_dependency(target_klass, *requirements)
|
||||
end
|
||||
end
|
||||
|
||||
#################
|
||||
# macro sub-builders
|
||||
|
||||
def create_join_association(association_id, reflection)
|
||||
|
||||
options = {:foreign_key => reflection.options[:foreign_key],
|
||||
:dependent => reflection.options[:dependent],
|
||||
:class_name => reflection.klass.name,
|
||||
:extend => reflection.options[:join_extend],
|
||||
# :limit => reflection.options[:limit],
|
||||
# :offset => reflection.options[:offset],
|
||||
:order => devolve(association_id, reflection, reflection.options[:order], reflection.klass),
|
||||
:conditions => devolve(association_id, reflection, reflection.options[:conditions], reflection.klass)
|
||||
}
|
||||
|
||||
if reflection.options[:foreign_type_key]
|
||||
type_check = "#{reflection.options[:foreign_type_key]} = #{quote_value(self.base_class.name)}"
|
||||
conjunction = options[:conditions] ? " AND " : nil
|
||||
options[:conditions] = "#{options[:conditions]}#{conjunction}#{type_check}"
|
||||
end
|
||||
|
||||
has_many(reflection.options[:through], options)
|
||||
inject_before_save_into_join_table(association_id, reflection)
|
||||
end
|
||||
|
||||
def inject_before_save_into_join_table(association_id, reflection)
|
||||
sti_hook = "sti_class_rewrite"
|
||||
rewrite_procedure = %[self.send(:#{association_id._singularize}_type=, self.#{association_id._singularize}_type.constantize.base_class.name)]
|
||||
|
||||
# XXX should be abstracted?
|
||||
reflection.klass.class_eval %[
|
||||
unless instance_methods.include? "before_save_with_#{sti_hook}"
|
||||
if instance_methods.include? "before_save"
|
||||
alias_method :before_save_without_#{sti_hook}, :before_save
|
||||
def before_save_with_#{sti_hook}
|
||||
before_save_without_#{sti_hook}
|
||||
#{rewrite_procedure}
|
||||
end
|
||||
else
|
||||
def before_save_with_#{sti_hook}
|
||||
#{rewrite_procedure}
|
||||
end
|
||||
end
|
||||
alias_method :before_save, :before_save_with_#{sti_hook}
|
||||
end
|
||||
]
|
||||
end
|
||||
|
||||
def create_has_many_through_associations_for_children_to_parent(association_id, reflection)
|
||||
|
||||
child_pluralization_map(association_id, reflection).each do |plural, singular|
|
||||
if singular == reflection.options[:as]
|
||||
raise PolymorphicError, if reflection.options[:is_double]
|
||||
"You can't give either of the sides in a double-polymorphic join the same name as any of the individual target classes."
|
||||
else
|
||||
"You can't have a self-referential polymorphic has_many :through without renaming the non-polymorphic foreign key in the join model."
|
||||
end
|
||||
end
|
||||
|
||||
parent = self
|
||||
plural._as_class.instance_eval do
|
||||
# this shouldn't be called at all during doubles; there is no way to traverse to a double polymorphic parent (XXX is that right?)
|
||||
unless reflection.options[:is_double] or reflection.options[:conflicts].include? self.name.tableize.to_sym
|
||||
|
||||
# the join table
|
||||
through = "#{reflection.options[:through]}#{'_as_child' if parent == self}".to_sym
|
||||
has_many(through,
|
||||
:as => association_id._singularize,
|
||||
:class_name => reflection.klass.name,
|
||||
:dependent => reflection.options[:dependent],
|
||||
:extend => reflection.options[:join_extend],
|
||||
# :limit => reflection.options[:limit],
|
||||
# :offset => reflection.options[:offset],
|
||||
:order => devolve(association_id, reflection, reflection.options[:order], reflection.klass),
|
||||
:conditions => devolve(association_id, reflection, reflection.options[:conditions], reflection.klass)
|
||||
)
|
||||
|
||||
# the association to the collection parents
|
||||
association = "#{reflection.options[:as]._pluralize}#{"_of_#{association_id}" if reflection.options[:rename_individual_collections]}".to_sym
|
||||
has_many(association,
|
||||
:through => through,
|
||||
:class_name => parent.name,
|
||||
:source => reflection.options[:as],
|
||||
:foreign_key => reflection.options[:foreign_key] ,
|
||||
:extend => reflection.options[:parent_extend],
|
||||
:order => reflection.options[:parent_order],
|
||||
:offset => reflection.options[:parent_offset],
|
||||
:limit => reflection.options[:parent_limit],
|
||||
:group => reflection.options[:parent_group])
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_has_many_through_associations_for_parent_to_children(association_id, reflection)
|
||||
child_pluralization_map(association_id, reflection).each do |plural, singular|
|
||||
#puts ":source => #{child}"
|
||||
current_association = demodulate(child_association_map(association_id, reflection)[plural])
|
||||
source = demodulate(singular)
|
||||
|
||||
if reflection.options[:conflicts].include? plural
|
||||
# XXX check this
|
||||
current_association = "#{association_id._singularize}_#{current_association}" if reflection.options[:conflicts].include? self.name.tableize.to_sym
|
||||
source = "#{source}_as_#{association_id._singularize}".to_sym
|
||||
end
|
||||
|
||||
# make push/delete accessible from the individual collections but still operate via the general collection
|
||||
extension_module = self.class_eval %[
|
||||
module #{self.name + current_association._classify + "PolymorphicChildAssociationExtension"}
|
||||
def push *args; proxy_owner.send(:#{association_id}).send(:push, *args).select{|x| x.is_a? #{singular._classify}}; end
|
||||
alias :<< :push
|
||||
def delete *args; proxy_owner.send(:#{association_id}).send(:delete, *args); end
|
||||
def clear; proxy_owner.send(:#{association_id}).send(:clear, #{singular._classify}); end
|
||||
self
|
||||
end]
|
||||
|
||||
has_many(current_association.to_sym,
|
||||
:through => reflection.options[:through],
|
||||
:source => association_id._singularize,
|
||||
:source_type => plural._as_class.base_class.name,
|
||||
:extend => (Array(extension_module) + reflection.options[:extend]),
|
||||
:limit => reflection.options[:limit],
|
||||
# :offset => reflection.options[:offset],
|
||||
:order => devolve(association_id, reflection, reflection.options[:order], plural._as_class),
|
||||
:conditions => devolve(association_id, reflection, reflection.options[:conditions], plural._as_class),
|
||||
:group => devolve(association_id, reflection, reflection.options[:group], plural._as_class)
|
||||
)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
##############
|
||||
# some support methods
|
||||
|
||||
def child_pluralization_map(association_id, reflection)
|
||||
Hash[*reflection.options[:from].map do |plural|
|
||||
[plural, plural._singularize]
|
||||
end.flatten]
|
||||
end
|
||||
|
||||
def child_association_map(association_id, reflection)
|
||||
Hash[*reflection.options[:from].map do |plural|
|
||||
[plural, "#{association_id._singularize.to_s + "_" if reflection.options[:rename_individual_collections]}#{plural}".to_sym]
|
||||
end.flatten]
|
||||
end
|
||||
|
||||
def demodulate(s)
|
||||
s.to_s.gsub('/', '_').to_sym
|
||||
end
|
||||
|
||||
def build_join_table_symbol(a, b)
|
||||
[a.to_s, b.to_s].sort.join("_").to_sym
|
||||
end
|
||||
|
||||
def all_classes_for(association_id, reflection)
|
||||
klasses = [self, reflection.klass, *child_pluralization_map(association_id, reflection).keys.map(&:_as_class)]
|
||||
klasses += klasses.map(&:base_class)
|
||||
klasses.uniq
|
||||
end
|
||||
|
||||
def devolve(association_id, reflection, string, klass)
|
||||
return unless string
|
||||
(all_classes_for(association_id, reflection) - # the join class must always be preserved
|
||||
[klass, klass.base_class, reflection.klass, reflection.klass.base_class]).map do |klass|
|
||||
klass.columns.map do |column|
|
||||
[klass.table_name, column.name]
|
||||
end.map do |table, column|
|
||||
["#{table}.#{column}", "`#{table}`.#{column}", "#{table}.`#{column}`", "`#{table}`.`#{column}`"]
|
||||
end
|
||||
end.flatten.sort_by(&:size).reverse.each do |quoted_reference|
|
||||
string.gsub!(quoted_reference, "NULL")
|
||||
end
|
||||
string
|
||||
end
|
||||
|
||||
def verify_pluralization_of(sym)
|
||||
sym = sym.to_s
|
||||
singular = sym.singularize
|
||||
plural = singular.pluralize
|
||||
raise PolymorphicError, "Pluralization rules not set up correctly. You passed :#{sym}, which singularizes to :#{singular}, but that pluralizes to :#{plural}, which is different. Maybe you meant :#{plural} to begin with?" unless sym == plural
|
||||
end
|
||||
|
||||
def spiked_create_extension_module(association_id, extensions, identifier = nil)
|
||||
module_extensions = extensions.select{|e| e.is_a? Module}
|
||||
proc_extensions = extensions.select{|e| e.is_a? Proc }
|
||||
|
||||
# support namespaced anonymous blocks as well as multiple procs
|
||||
proc_extensions.each_with_index do |proc_extension, index|
|
||||
module_name = "#{self.to_s}#{association_id._classify}Polymorphic#{identifier}AssociationExtension#{index}"
|
||||
the_module = self.class_eval "module #{module_name}; self; end" # haha
|
||||
the_module.class_eval &proc_extension
|
||||
module_extensions << the_module
|
||||
end
|
||||
module_extensions
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
18
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/configuration.rb
vendored
Normal file
18
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/configuration.rb
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
### deprecated
|
||||
|
||||
if defined? Rails::Configuration
|
||||
class Rails::Configuration
|
||||
def has_many_polymorphs_cache_classes= *args
|
||||
::ActiveRecord::Associations::ClassMethods.has_many_polymorphs_cache_classes = *args
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ActiveRecord
|
||||
module Associations
|
||||
module ClassMethods
|
||||
mattr_accessor :has_many_polymorphs_cache_classes
|
||||
end
|
||||
end
|
||||
end
|
||||
72
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/debugging_tools.rb
vendored
Normal file
72
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/debugging_tools.rb
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
|
||||
class << ActiveRecord::Base
|
||||
COLLECTION_METHODS = [:belongs_to, :has_many, :has_and_belongs_to_many, :has_one,
|
||||
:has_many_polymorphs, :acts_as_double_polymorphic_join].each do |method_name|
|
||||
alias_method "original_#{method_name}".to_sym, method_name
|
||||
undef_method method_name
|
||||
end
|
||||
|
||||
unless defined? GENERATED_CODE_DIR
|
||||
# automatic code generation for debugging
|
||||
# you will get a folder "generated_models" in RAILS_ROOT containing valid Ruby files
|
||||
# explaining all ActiveRecord relationships set up by the plugin, as well as listing the
|
||||
# line in the plugin that made each particular macro call
|
||||
GENERATED_CODE_DIR = "#{RAILS_ROOT}/generated_models"
|
||||
|
||||
begin
|
||||
system "rm -rf #{GENERATED_CODE_DIR}"
|
||||
Dir.mkdir GENERATED_CODE_DIR
|
||||
rescue Errno::EACCES
|
||||
_logger_warn "no permissions for generated code dir: #{GENERATED_CODE_DIR}"
|
||||
end
|
||||
|
||||
if File.exist? GENERATED_CODE_DIR
|
||||
alias :original_method_missing :method_missing
|
||||
def method_missing(method_name, *args, &block)
|
||||
if COLLECTION_METHODS.include? method_name.to_sym
|
||||
Dir.chdir GENERATED_CODE_DIR do
|
||||
filename = "#{demodulate(self.name.underscore)}.rb"
|
||||
contents = File.open(filename).read rescue "\nclass #{self.name}\n\nend\n"
|
||||
line = caller[1][/\:(\d+)\:/, 1]
|
||||
contents[-5..-5] = "\n #{method_name} #{args[0..-2].inspect[1..-2]},\n #{args[-1].inspect[1..-2].gsub(" :", "\n :").gsub("=>", " => ")}\n#{ block ? " #{block.inspect.sub(/\@.*\//, '@')}\n" : ""} # called from line #{line}\n\n"
|
||||
File.open(filename, "w") do |file|
|
||||
file.puts contents
|
||||
end
|
||||
end
|
||||
# doesn't handle blocks cause we can't introspect on code like that in Ruby without hackery and dependencies
|
||||
self.send("original_#{method_name}", *args, &block)
|
||||
else
|
||||
self.send(:original_method_missing, method_name, *args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# and have a debugger enabled
|
||||
case ENV['DEBUG']
|
||||
when "ruby-debug"
|
||||
require 'rubygems'
|
||||
require 'ruby-debug'
|
||||
Debugger.start
|
||||
puts "Notice; ruby-debug enabled."
|
||||
when "trace"
|
||||
puts "Notice; method tracing enabled"
|
||||
$debug_trace_indent = 0
|
||||
set_trace_func (proc do |event, file, line, id, binding, classname|
|
||||
if id.to_s =~ /instantiate/ #/IRB|Wirble|RubyLex|RubyToken|Logger|ConnectionAdapters|SQLite3|MonitorMixin|Benchmark|Inflector|Inflections/
|
||||
if event == 'call'
|
||||
puts (" " * $debug_trace_indent) + "#{event}ed #{classname}\##{id} from #{file.split('/').last}::#{line}"
|
||||
$debug_trace_indent += 1
|
||||
elsif event == 'return'
|
||||
$debug_trace_indent -= 1 unless $debug_trace_indent == 0
|
||||
puts (" " * $debug_trace_indent) + "#{event}ed #{classname}\##{id}"
|
||||
end
|
||||
end
|
||||
end)
|
||||
when "dependencies"
|
||||
puts "Notice; dependency activity being logged"
|
||||
(::Dependencies.log_activity = true) rescue nil
|
||||
end
|
||||
30
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/dependencies.rb
vendored
Normal file
30
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/dependencies.rb
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
module Dependencies
|
||||
|
||||
#### dependency injection
|
||||
|
||||
mattr_accessor :injection_graph
|
||||
self.injection_graph = Hash.new([])
|
||||
|
||||
def inject_dependency(target, *requirements)
|
||||
target, requirements = target.to_s, requirements.map(&:to_s)
|
||||
injection_graph[target] = ((injection_graph[target] + requirements).uniq - [target])
|
||||
requirements.each {|requirement| mark_for_unload requirement }
|
||||
# _logger_debug "has_many_polymorphs: injection graph: #{injection_graph.inspect}"
|
||||
end
|
||||
|
||||
def new_constants_in_with_injection(*descs, &block) # chain
|
||||
# _logger_debug "has_many_polymorphs: NEW: autoloaded constants: #{autoloaded_constants.inspect}; #{explicitly_unloadable_constants.inspect}" if (autoloaded_constants + explicitly_unloadable_constants).any?
|
||||
returning(new_constants_in_without_injection(*descs, &block)) do |found|
|
||||
# _logger_debug "has_many_polymorphs: new constants: #{found.inspect}" if found.any?
|
||||
found.each do |constant|
|
||||
injection_graph[constant].each do |requirement|
|
||||
requirement.constantize
|
||||
# _logger_debug "has_many_polymorphs: constantized #{requirement}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
alias_method_chain :new_constants_in, :injection
|
||||
|
||||
end
|
||||
25
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/rake_task_redefine_task.rb
vendored
Normal file
25
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/rake_task_redefine_task.rb
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
# http://www.bigbold.com/snippets/posts/show/2032
|
||||
module Rake
|
||||
module TaskManager
|
||||
def redefine_task(task_class, args, &block)
|
||||
task_name, deps = resolve_args(args)
|
||||
task_name = task_class.scope_name(@scope, task_name)
|
||||
deps = [deps] unless deps.respond_to?(:to_ary)
|
||||
deps = deps.collect {|d| d.to_s }
|
||||
task = @tasks[task_name.to_s] = task_class.new(task_name, self)
|
||||
task.application = self
|
||||
task.add_comment(@last_comment)
|
||||
@last_comment = nil
|
||||
task.enhance(deps, &block)
|
||||
task
|
||||
end
|
||||
end
|
||||
class Task
|
||||
class << self
|
||||
def redefine_task(args, &block)
|
||||
Rake.application.redefine_task(self, args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
41
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/reflection.rb
vendored
Normal file
41
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/reflection.rb
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
module ActiveRecord
|
||||
module Reflection
|
||||
|
||||
module ClassMethods
|
||||
def create_reflection(macro, name, options, active_record)
|
||||
case macro
|
||||
when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
|
||||
reflection = AssociationReflection.new(macro, name, options, active_record)
|
||||
when :composed_of
|
||||
reflection = AggregateReflection.new(macro, name, options, active_record)
|
||||
######
|
||||
when :has_many_polymorphs
|
||||
reflection = PolymorphicReflection.new(macro, name, options, active_record)
|
||||
######
|
||||
end
|
||||
write_inheritable_hash :reflections, name => reflection
|
||||
reflection
|
||||
end
|
||||
end
|
||||
|
||||
class PolymorphicError < ActiveRecordError
|
||||
end
|
||||
|
||||
class PolymorphicReflection < AssociationReflection
|
||||
def check_validity!
|
||||
# nothing
|
||||
end
|
||||
|
||||
# these are kind of shady but it lets us inherit more directly
|
||||
def source_reflection
|
||||
self
|
||||
end
|
||||
|
||||
def class_name
|
||||
@class_name ||= options[:join_class_name]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
57
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/support_methods.rb
vendored
Normal file
57
tracks/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/support_methods.rb
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
# hope these don't mess anyone up
|
||||
|
||||
class String
|
||||
def _as_class
|
||||
# classify expects self to be plural
|
||||
self.classify.constantize
|
||||
end
|
||||
# def _as_base_class; _as_class.base_class; end
|
||||
alias :_singularize :singularize
|
||||
alias :_pluralize :pluralize
|
||||
alias :_classify :classify
|
||||
end
|
||||
|
||||
class Symbol
|
||||
def _as_class; self.to_s._as_class; end
|
||||
# def _as_base_class; self.to_s._as_base_class; end
|
||||
def _singularize; self.to_s.singularize.to_sym; end
|
||||
def _pluralize; self.to_s.pluralize.to_sym; end
|
||||
def _classify; self.to_s.classify; end
|
||||
end
|
||||
|
||||
class Array
|
||||
def _flatten_once
|
||||
self.inject([]){|r, el| r + Array(el)}
|
||||
end
|
||||
end
|
||||
|
||||
class Hash
|
||||
def _select
|
||||
Hash[*self.select do |key, value|
|
||||
yield key, value
|
||||
end._flatten_once]
|
||||
end
|
||||
end
|
||||
|
||||
class Object
|
||||
def _metaclass; (class << self; self; end); end
|
||||
|
||||
def _logger_debug s
|
||||
::ActiveRecord::Base.logger.debug(s) if ::ActiveRecord::Base.logger
|
||||
end
|
||||
def _logger_warn s
|
||||
if ::ActiveRecord::Base.logger
|
||||
::ActiveRecord::Base.logger.warn(s)
|
||||
else
|
||||
$stderr.puts("has_many_polymorphs: #{s}")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class ActiveRecord::Base
|
||||
def _base_class_name
|
||||
self.class.base_class.name.to_s
|
||||
end
|
||||
end
|
||||
|
|
@ -2,7 +2,11 @@ swimmy:
|
|||
id: 1
|
||||
name: Swimmy
|
||||
speed: 10
|
||||
created_at: "2007-02-01 12:00:00"
|
||||
updated_at: "2007-02-04 10:00:00"
|
||||
jaws:
|
||||
id: 2
|
||||
name: Jaws
|
||||
speed: 20
|
||||
speed: 20
|
||||
created_at: "2007-02-02 12:00:00"
|
||||
updated_at: "2007-02-03 10:00:00"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
shamu:
|
||||
id: 1
|
||||
name: Shamu
|
||||
created_at: "2007-03-01 12:00:00"
|
||||
updated_at: "2007-03-04 10:00:00"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
rover:
|
||||
id: 1
|
||||
name: Rover
|
||||
created_at: "2007-01-01 12:00:00"
|
||||
updated_at: "2007-01-04 10:00:00"
|
||||
spot:
|
||||
id: 2
|
||||
name: Spot
|
||||
name: Spot
|
||||
created_at: "2007-01-02 12:00:00"
|
||||
updated_at: "2007-01-03 10:00:00"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ chloe:
|
|||
id: 1
|
||||
cat_type: Kitten
|
||||
name: Chloe
|
||||
created_at: "2007-04-01 12:00:00"
|
||||
updated_at: "2007-04-04 10:00:00"
|
||||
alice:
|
||||
id: 2
|
||||
cat_type: Kitten
|
||||
name: Alice
|
||||
name: Alice
|
||||
created_at: "2007-04-02 12:00:00"
|
||||
updated_at: "2007-04-03 10:00:00"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
froggy:
|
||||
id: 1
|
||||
name: Froggy
|
||||
created_at: "2007-05-01 12:00:00"
|
||||
updated_at: "2007-05-04 10:00:00"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
kibbles:
|
||||
the_petfood_primary_key: 1
|
||||
name: Kibbles
|
||||
created_at: "2007-06-01 12:00:00"
|
||||
updated_at: "2007-06-04 10:00:00"
|
||||
bits:
|
||||
the_petfood_primary_key: 2
|
||||
name: Bits
|
||||
name: Bits
|
||||
created_at: "2007-06-02 12:00:00"
|
||||
updated_at: "2007-06-03 10:00:00"
|
||||
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
puma:
|
||||
id: 1
|
||||
name: Puma
|
||||
created_at: "2007-07-01 12:00:00"
|
||||
updated_at: "2007-07-04 10:00:00"
|
||||
jacrazy:
|
||||
id: 2
|
||||
name: Jacrazy
|
||||
created_at: "2007-07-02 12:00:00"
|
||||
updated_at: "2007-07-03 10:00:00"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ require 'aquatic/pupils_whale'
|
|||
|
||||
class Aquatic::Whale < ActiveRecord::Base
|
||||
has_many_polymorphs(:aquatic_pupils, :from => [:dogs, :"aquatic/fish"],
|
||||
:through => "aquatic/pupils_whales") do
|
||||
def blow; "result"; end
|
||||
end
|
||||
:through => "aquatic/pupils_whales") do
|
||||
def a_method
|
||||
:correct_block_result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
require 'extension_module'
|
||||
|
||||
class BeautifulFightRelationship < ActiveRecord::Base
|
||||
set_table_name 'keep_your_enemies_close'
|
||||
|
||||
|
|
@ -8,6 +10,17 @@ class BeautifulFightRelationship < ActiveRecord::Base
|
|||
# are not supported by Rails
|
||||
|
||||
acts_as_double_polymorphic_join :enemies => [:dogs, :kittens, :frogs],
|
||||
:protectors => [:wild_boars, :kittens, :"aquatic/fish", :dogs]
|
||||
:protectors => [:wild_boars, :kittens, :"aquatic/fish", :dogs],
|
||||
:enemies_extend => [ExtensionModule, proc {}],
|
||||
:protectors_extend => proc {
|
||||
def a_method
|
||||
:correct_proc_result
|
||||
end
|
||||
},
|
||||
:join_extend => proc {
|
||||
def a_method
|
||||
:correct_join_result
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
9
tracks/vendor/plugins/has_many_polymorphs/test/models/canine.rb
vendored
Normal file
9
tracks/vendor/plugins/has_many_polymorphs/test/models/canine.rb
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
class Canine < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
|
||||
def an_abstract_method
|
||||
:correct_abstract_method_response
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
class Dog < ActiveRecord::Base
|
||||
attr_accessor :after_find_test, :after_initialize_test
|
||||
|
||||
require 'canine'
|
||||
|
||||
class Dog < Canine
|
||||
attr_accessor :after_find_test, :after_initialize_test
|
||||
set_table_name "bow_wows"
|
||||
|
||||
def after_find
|
||||
|
|
|
|||
|
|
@ -8,14 +8,31 @@ require 'dog'
|
|||
require 'wild_boar'
|
||||
require 'kitten'
|
||||
require 'tabby'
|
||||
require 'extension_module'
|
||||
require 'other_extension_module'
|
||||
|
||||
class Petfood < ActiveRecord::Base
|
||||
set_primary_key 'the_petfood_primary_key'
|
||||
has_many_polymorphs :eaters,
|
||||
:from => [:dogs, :petfoods, :wild_boars, :kittens,
|
||||
:tabbies, :"aquatic/fish"],
|
||||
:dependent => :destroy,
|
||||
:rename_individual_collections => true,
|
||||
:acts_as => :foodstuff,
|
||||
:foreign_key => "foodstuff_id"
|
||||
end
|
||||
:from => [:dogs, :petfoods, :wild_boars, :kittens,
|
||||
:tabbies, :"aquatic/fish"],
|
||||
# :dependent => :destroy, :destroy is now the default
|
||||
:rename_individual_collections => true,
|
||||
:as => :foodstuff,
|
||||
:foreign_key => "foodstuff_id",
|
||||
:ignore_duplicates => false,
|
||||
:conditions => "NULL IS NULL",
|
||||
:order => "eaters_foodstuffs.updated_at ASC",
|
||||
:parent_order => "the_petfood_primary_key DESC",
|
||||
:extend => [ExtensionModule, OtherExtensionModule, proc {}],
|
||||
:join_extend => proc {
|
||||
def a_method
|
||||
:correct_join_result
|
||||
end
|
||||
},
|
||||
:parent_extend => proc {
|
||||
def a_method
|
||||
:correct_parent_proc_result
|
||||
end
|
||||
}
|
||||
end
|
||||
|
|
|
|||
9
tracks/vendor/plugins/has_many_polymorphs/test/modules/extension_module.rb
vendored
Normal file
9
tracks/vendor/plugins/has_many_polymorphs/test/modules/extension_module.rb
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
module ExtensionModule
|
||||
def a_method
|
||||
:correct_module_result
|
||||
end
|
||||
def self.a_method
|
||||
:incorrect_module_result
|
||||
end
|
||||
end
|
||||
9
tracks/vendor/plugins/has_many_polymorphs/test/modules/other_extension_module.rb
vendored
Normal file
9
tracks/vendor/plugins/has_many_polymorphs/test/modules/other_extension_module.rb
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
module OtherExtensionModule
|
||||
def another_method
|
||||
:correct_other_module_result
|
||||
end
|
||||
def self.another_method
|
||||
:incorrect_other_module_result
|
||||
end
|
||||
end
|
||||
|
|
@ -1,23 +1,33 @@
|
|||
ActiveRecord::Schema.define(:version => 0) do
|
||||
create_table :petfoods, :force => true, :primary_key => :the_petfood_primary_key do |t|
|
||||
t.column :name, :string
|
||||
t.column :created_at, :datetime, :null => false
|
||||
t.column :updated_at, :datetime, :null => false
|
||||
end
|
||||
|
||||
create_table :bow_wows, :force => true do |t|
|
||||
t.column :name, :string
|
||||
t.column :created_at, :datetime, :null => false
|
||||
t.column :updated_at, :datetime, :null => false
|
||||
end
|
||||
|
||||
create_table :cats, :force => true do |t|
|
||||
t.column :name, :string
|
||||
t.column :cat_type, :string
|
||||
t.column :created_at, :datetime, :null => false
|
||||
t.column :updated_at, :datetime, :null => false
|
||||
end
|
||||
|
||||
create_table :frogs, :force => true do |t|
|
||||
t.column :name, :string
|
||||
t.column :created_at, :datetime, :null => false
|
||||
t.column :updated_at, :datetime, :null => false
|
||||
end
|
||||
|
||||
create_table :wild_boars, :force => true do |t|
|
||||
t.column :name, :string
|
||||
t.column :created_at, :datetime, :null => false
|
||||
t.column :updated_at, :datetime, :null => false
|
||||
end
|
||||
|
||||
create_table :eaters_foodstuffs, :force => true do |t|
|
||||
|
|
@ -25,21 +35,29 @@ ActiveRecord::Schema.define(:version => 0) do
|
|||
t.column :eater_id, :integer
|
||||
t.column :some_attribute, :integer, :default => 0
|
||||
t.column :eater_type, :string
|
||||
t.column :created_at, :datetime, :null => false
|
||||
t.column :updated_at, :datetime, :null => false
|
||||
end
|
||||
|
||||
create_table :fish, :force => true do |t|
|
||||
t.column :name, :string
|
||||
t.column :speed, :integer
|
||||
t.column :created_at, :datetime, :null => false
|
||||
t.column :updated_at, :datetime, :null => false
|
||||
end
|
||||
|
||||
create_table :whales, :force => true do |t|
|
||||
t.column :name, :string
|
||||
t.column :created_at, :datetime, :null => false
|
||||
t.column :updated_at, :datetime, :null => false
|
||||
end
|
||||
|
||||
create_table :little_whale_pupils, :force => true do |t|
|
||||
t.column :whale_id, :integer
|
||||
t.column :aquatic_pupil_id, :integer
|
||||
t.column :aquatic_pupil_type, :string
|
||||
t.column :created_at, :datetime, :null => false
|
||||
t.column :updated_at, :datetime, :null => false
|
||||
end
|
||||
|
||||
create_table :keep_your_enemies_close, :force => true do |t|
|
||||
|
|
@ -47,6 +65,8 @@ ActiveRecord::Schema.define(:version => 0) do
|
|||
t.column :enemy_type, :string
|
||||
t.column :protector_id, :integer
|
||||
t.column :protector_type, :string
|
||||
t.column :created_at, :datetime, :null => false
|
||||
t.column :updated_at, :datetime, :null => false
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,20 +1,31 @@
|
|||
require 'pathname'
|
||||
# default test helper
|
||||
|
||||
begin
|
||||
require 'rubygems'
|
||||
require 'ruby-debug'
|
||||
Debugger.start
|
||||
rescue Object
|
||||
end
|
||||
|
||||
# load the applicaiton's test helper
|
||||
begin
|
||||
require File.dirname(__FILE__) + '/../../../../test/test_helper'
|
||||
rescue LoadError
|
||||
require '~/projects/miscellaneous/cookbook/test/test_helper'
|
||||
end
|
||||
|
||||
WORKING_DIR = File.dirname(__FILE__)
|
||||
|
||||
Inflector.inflections {|i| i.irregular 'fish', 'fish' }
|
||||
|
||||
# fixtures
|
||||
$LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/")
|
||||
$LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path = WORKING_DIR + "/fixtures")
|
||||
# models
|
||||
$LOAD_PATH.unshift("#{Pathname.new(__FILE__).dirname.to_s}/models")
|
||||
$LOAD_PATH.unshift(WORKING_DIR + "/models")
|
||||
# extension modules
|
||||
$LOAD_PATH.unshift(WORKING_DIR + "/modules")
|
||||
|
||||
class Test::Unit::TestCase
|
||||
self.use_transactional_fixtures = true # must stay true for tests to run on postgres or sqlite3
|
||||
self.use_transactional_fixtures = (not ActiveRecord::Base.connection.is_a? ActiveRecord::ConnectionAdapters::MysqlAdapter)
|
||||
self.use_instantiated_fixtures = false
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,9 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
:"aquatic/fish", :"aquatic/whales", :"aquatic/little_whale_pupils",
|
||||
:keep_your_enemies_close
|
||||
require 'beautiful_fight_relationship'
|
||||
|
||||
# to-do: finder queries on the collection
|
||||
# order-mask column on the join table for polymorphic order
|
||||
# rework load order so you could push and pop without ever loading the whole collection
|
||||
# so that limit works in a sane way
|
||||
|
||||
|
||||
def setup
|
||||
@association_error = ActiveRecord::Associations::PolymorphicError
|
||||
@kibbles = Petfood.find(1)
|
||||
@bits = Petfood.find(2)
|
||||
@shamu = Aquatic::Whale.find(1)
|
||||
|
|
@ -25,8 +21,8 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
@froggy = Frog.find(1)
|
||||
|
||||
@join_count = EatersFoodstuff.count
|
||||
@l = @kibbles.eaters.length
|
||||
@m = @bits.eaters.count
|
||||
@l = @kibbles.eaters.size
|
||||
@m = @bits.eaters.size
|
||||
end
|
||||
|
||||
def test_all_relationship_validities
|
||||
|
|
@ -53,21 +49,16 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
assert_equal @l += 1, @kibbles.eaters.count
|
||||
|
||||
@kibbles.reload
|
||||
assert_equal @l, @kibbles.eaters.count
|
||||
|
||||
assert_equal @l, @kibbles.eaters.count
|
||||
end
|
||||
|
||||
def test_duplicate_assignment
|
||||
# try to add a duplicate item
|
||||
# try to add a duplicate item when :ignore_duplicates is false
|
||||
@kibbles.eaters.push(@alice)
|
||||
assert @kibbles.eaters.include?(@alice)
|
||||
@kibbles.eaters.push(@alice)
|
||||
assert_equal @l + 1, @kibbles.eaters.count
|
||||
assert_equal @join_count + 1, EatersFoodstuff.count
|
||||
|
||||
@kibbles.reload
|
||||
assert_equal @l + 1, @kibbles.eaters.count
|
||||
assert_equal @join_count + 1, EatersFoodstuff.count
|
||||
assert_equal @l + 2, @kibbles.eaters.count
|
||||
assert_equal @join_count + 2, EatersFoodstuff.count
|
||||
end
|
||||
|
||||
def test_create_and_push
|
||||
|
|
@ -97,31 +88,27 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
assert @join_record.id
|
||||
assert_equal @join_count + 1, EatersFoodstuff.count
|
||||
|
||||
# has the parent changed if we don't reload?
|
||||
assert_equal @m, @bits.eaters.count
|
||||
|
||||
# if we do reload, is the new association there?
|
||||
# XXX no, because TestCase breaks reload. it works fine in the app.
|
||||
# not reloaded
|
||||
assert_equal @m, @bits.eaters.size
|
||||
assert_equal @m + 1, @bits.eaters.count # SQL :)
|
||||
|
||||
assert_equal Petfood, @bits.eaters.reload.class
|
||||
assert_equal @m + 1, @bits.eaters.count
|
||||
# is the new association there?
|
||||
assert @bits.eaters.reload
|
||||
assert @bits.eaters.include?(@chloe)
|
||||
|
||||
# puts "XXX #{EatersFoodstuff.count}"
|
||||
|
||||
end
|
||||
|
||||
def test_add_unsaved
|
||||
# add an unsaved item
|
||||
assert @bits.eaters << Kitten.new(:name => "Bridget")
|
||||
assert_nil Kitten.find_by_name("Bridget")
|
||||
assert_equal @m + 1, @bits.eaters.count
|
||||
|
||||
assert @bits.save
|
||||
@bits.reload
|
||||
assert_equal @m + 1, @bits.eaters.count
|
||||
|
||||
end
|
||||
# not supporting this, since has_many :through doesn't support it either
|
||||
# def test_add_unsaved
|
||||
# # add an unsaved item
|
||||
# assert @bits.eaters << Kitten.new(:name => "Bridget")
|
||||
# assert_nil Kitten.find_by_name("Bridget")
|
||||
# assert_equal @m + 1, @bits.eaters.count
|
||||
#
|
||||
# assert @bits.save
|
||||
# @bits.reload
|
||||
# assert_equal @m + 1, @bits.eaters.count
|
||||
#
|
||||
# end
|
||||
|
||||
def test_self_reference
|
||||
assert @kibbles.eaters << @bits
|
||||
|
|
@ -154,11 +141,10 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
def test_clear
|
||||
@kibbles.eaters << [@chloe, @spot, @rover]
|
||||
@kibbles.reload
|
||||
assert_equal 3, @kibbles.eaters.clear.size
|
||||
assert @kibbles.eaters.clear.blank?
|
||||
assert @kibbles.eaters.blank?
|
||||
@kibbles.reload
|
||||
assert @kibbles.eaters.blank?
|
||||
assert_equal 0, @kibbles.eaters.clear.size
|
||||
end
|
||||
|
||||
def test_individual_collections
|
||||
|
|
@ -169,7 +155,7 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
assert 1, @rover.eaters_foodstuffs.count
|
||||
end
|
||||
|
||||
def test_invididual_collections_push
|
||||
def test_individual_collections_push
|
||||
assert_equal [@chloe], (@kibbles.eater_kittens << @chloe)
|
||||
@kibbles.reload
|
||||
assert @kibbles.eaters.include?(@chloe)
|
||||
|
|
@ -177,24 +163,31 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
assert !@kibbles.eater_dogs.include?(@chloe)
|
||||
end
|
||||
|
||||
def test_invididual_collections_delete
|
||||
def test_individual_collections_delete
|
||||
@kibbles.eaters << [@chloe, @spot, @rover]
|
||||
@kibbles.reload
|
||||
assert_equal [@chloe], @kibbles.eater_kittens.delete(@chloe)
|
||||
assert @kibbles.eater_kittens.empty?
|
||||
assert !@kibbles.eater_kittens.delete(@chloe)
|
||||
@kibbles.eater_kittens.delete(@chloe) # what should this return?
|
||||
|
||||
@kibbles.reload
|
||||
assert @kibbles.eater_kittens.empty?
|
||||
assert @kibbles.eater_dogs.include?(@spot)
|
||||
end
|
||||
|
||||
def test_invididual_collections_clear
|
||||
def test_individual_collections_clear
|
||||
@kibbles.eaters << [@chloe, @spot, @rover]
|
||||
@kibbles.reload
|
||||
assert_equal [@chloe], @kibbles.eater_kittens.clear
|
||||
|
||||
assert_equal [], @kibbles.eater_kittens.clear
|
||||
assert @kibbles.eater_kittens.empty?
|
||||
assert_equal 2, @kibbles.eaters.size
|
||||
|
||||
assert @kibbles.eater_kittens.empty?
|
||||
assert_equal 2, @kibbles.eaters.size
|
||||
assert !@kibbles.eater_kittens.include?(@chloe)
|
||||
assert !@kibbles.eaters.include?(@chloe)
|
||||
|
||||
@kibbles.reload
|
||||
assert @kibbles.eater_kittens.empty?
|
||||
assert_equal 2, @kibbles.eaters.size
|
||||
|
|
@ -229,26 +222,20 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
|
||||
def test_normal_callbacks
|
||||
assert @rover.respond_to?(:after_initialize)
|
||||
assert @rover.respond_to?(:after_find)
|
||||
|
||||
assert @rover.respond_to?(:after_find)
|
||||
assert @rover.after_initialize_test
|
||||
assert @rover.after_find_test
|
||||
end
|
||||
|
||||
def test_our_callbacks
|
||||
def test_model_callbacks_not_overridden_by_plugin_callbacks
|
||||
assert 0, @bits.eaters.count
|
||||
assert @bits.eaters.push(@rover)
|
||||
@bits.save
|
||||
|
||||
# puts "Testing callbacks."
|
||||
@bits2 = Petfood.find_by_name("Bits")
|
||||
@bits.reload
|
||||
|
||||
assert rover = @bits2.eaters.select { |x| x.name == "Rover" }[0]
|
||||
assert rover.after_initialize_test
|
||||
assert rover.after_find_test
|
||||
# puts "Done."
|
||||
|
||||
end
|
||||
|
||||
def test_number_of_join_records
|
||||
|
|
@ -268,8 +255,8 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
@join_record.save!
|
||||
@bits.eaters.reload
|
||||
|
||||
assert_equal 'Puma', @puma.name
|
||||
assert_equal 'Puma', @bits.eaters.first.name
|
||||
assert_equal "Puma", @puma.name
|
||||
assert_equal "Puma", @bits.eaters.first.name
|
||||
end
|
||||
|
||||
def test_before_save_on_join_table_is_not_clobbered_by_sti_base_class_fix
|
||||
|
|
@ -347,6 +334,10 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
assert_equal 3, @alice.beautiful_fight_relationships.size
|
||||
end
|
||||
|
||||
def test_double_dependency_injection
|
||||
# breakpoint
|
||||
end
|
||||
|
||||
def test_double_collection_deletion
|
||||
@alice.enemies << @spot
|
||||
@alice.reload
|
||||
|
|
@ -432,17 +423,12 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
assert !@spot.protectors.include?(@alice)
|
||||
end
|
||||
|
||||
def test_hmp_passed_block_manipulates_proxy_class
|
||||
assert_equal "result", @shamu.aquatic_pupils.blow
|
||||
assert_raises(NoMethodError) { @kibbles.eaters.blow }
|
||||
end
|
||||
|
||||
def test_collection_query_on_unsaved_record
|
||||
assert Dog.new.enemies.empty?
|
||||
assert Dog.new.foodstuffs_of_eaters.empty?
|
||||
end
|
||||
|
||||
def test_double_invididual_collections_push
|
||||
def test_double_individual_collections_push
|
||||
assert_equal [@chloe], (@spot.protector_kittens << @chloe)
|
||||
@spot.reload
|
||||
assert @spot.protectors.include?(@chloe)
|
||||
|
|
@ -456,22 +442,22 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
assert !@spot.enemy_dogs.include?(@froggy)
|
||||
end
|
||||
|
||||
def test_double_invididual_collections_delete
|
||||
def test_double_individual_collections_delete
|
||||
@spot.protectors << [@chloe, @puma]
|
||||
@spot.reload
|
||||
assert_equal [@chloe], @spot.protector_kittens.delete(@chloe)
|
||||
assert @spot.protector_kittens.empty?
|
||||
assert !@spot.protector_kittens.delete(@chloe)
|
||||
@spot.protector_kittens.delete(@chloe) # again, unclear what .delete should return
|
||||
|
||||
@spot.reload
|
||||
assert @spot.protector_kittens.empty?
|
||||
assert @spot.wild_boars.include?(@puma)
|
||||
end
|
||||
|
||||
def test_double_invididual_collections_clear
|
||||
def test_double_individual_collections_clear
|
||||
@spot.protectors << [@chloe, @puma, @alice]
|
||||
@spot.reload
|
||||
assert_equal [@chloe, @alice], @spot.protector_kittens.clear.sort_by(&:id)
|
||||
assert_equal [], @spot.protector_kittens.clear
|
||||
assert @spot.protector_kittens.empty?
|
||||
assert_equal 1, @spot.protectors.size
|
||||
@spot.reload
|
||||
|
|
@ -481,7 +467,156 @@ class PolymorphTest < Test::Unit::TestCase
|
|||
assert !@spot.protectors.include?(@chloe)
|
||||
assert !@spot.protector_kittens.include?(@alice)
|
||||
assert !@spot.protectors.include?(@alice)
|
||||
assert @spot.protectors.include?(@puma)
|
||||
assert @spot.wild_boars.include?(@puma)
|
||||
end
|
||||
|
||||
def test_single_extensions
|
||||
assert_equal :correct_block_result, @shamu.aquatic_pupils.a_method
|
||||
@kibbles.eaters.push(@alice)
|
||||
@kibbles.eaters.push(@spot)
|
||||
assert_equal :correct_join_result, @kibbles.eaters_foodstuffs.a_method
|
||||
assert_equal :correct_module_result, @kibbles.eaters.a_method
|
||||
assert_equal :correct_other_module_result, @kibbles.eaters.another_method
|
||||
@kibbles.eaters.each do |eater|
|
||||
assert_equal :correct_join_result, eater.eaters_foodstuffs.a_method
|
||||
end
|
||||
assert_equal :correct_parent_proc_result, @kibbles.foodstuffs_of_eaters.a_method
|
||||
assert_equal :correct_parent_proc_result, @kibbles.eaters.first.foodstuffs_of_eaters.a_method
|
||||
end
|
||||
|
||||
def test_double_extensions
|
||||
assert_equal :correct_proc_result, @spot.protectors.a_method
|
||||
assert_equal :correct_module_result, @spot.enemies.a_method
|
||||
assert_equal :correct_join_result, @spot.beautiful_fight_relationships_as_enemy.a_method
|
||||
assert_equal :correct_join_result, @spot.beautiful_fight_relationships_as_protector.a_method
|
||||
assert_equal :correct_join_result, @froggy.beautiful_fight_relationships.a_method
|
||||
assert_equal :correct_join_result, @froggy.beautiful_fight_relationships_as_enemy.a_method
|
||||
assert_raises(NoMethodError) {@froggy.beautiful_fight_relationships_as_protector.a_method}
|
||||
end
|
||||
|
||||
def test_pluralization_checks
|
||||
assert_raises(@association_error) {
|
||||
eval "class SomeModel < ActiveRecord::Base
|
||||
has_many_polymorphs :polymorphs, :from => [:dog, :cats]
|
||||
end" }
|
||||
assert_raises(@association_error) {
|
||||
eval "class SomeModel < ActiveRecord::Base
|
||||
has_many_polymorphs :polymorph, :from => [:dogs, :cats]
|
||||
end" }
|
||||
assert_raises(@association_error) {
|
||||
eval "class SomeModel < ActiveRecord::Base
|
||||
acts_as_double_polymorphic_join :polymorph => [:dogs, :cats], :unimorphs => [:dogs, :cats]
|
||||
end" }
|
||||
end
|
||||
|
||||
def test_single_custom_finders
|
||||
[@kibbles, @alice, @puma, @spot, @bits].each {|record| @kibbles.eaters << record; sleep 1} # XXX yeah i know
|
||||
assert_equal @kibbles.eaters, @kibbles.eaters.find(:all, :order => "eaters_foodstuffs.created_at ASC")
|
||||
assert_equal @kibbles.eaters.reverse, @kibbles.eaters.find(:all, :order => "eaters_foodstuffs.created_at DESC")
|
||||
if ActiveRecord::Base.connection.is_a? ActiveRecord::ConnectionAdapters::MysqlAdapter
|
||||
assert_equal @kibbles.eaters.sort_by(&:created_at), @kibbles.eaters.find(:all, :order => "IFNULL(bow_wows.created_at,(IFNULL(petfoods.created_at,(IFNULL(wild_boars.created_at,(IFNULL(cats.created_at,fish.created_at))))))) ASC")
|
||||
end
|
||||
assert_equal @kibbles.eaters.select{|x| x.is_a? Petfood}, @kibbles.eater_petfoods.find(:all, :order => "eaters_foodstuffs.created_at ASC")
|
||||
end
|
||||
|
||||
def test_double_custom_finders
|
||||
@spot.protectors << [@chloe, @puma, @alice]
|
||||
assert_equal [@chloe], @spot.protectors.find(:all, :conditions => ["cats.name = ?", @chloe.name], :limit => 1)
|
||||
assert_equal [], @spot.protectors.find(:all, :conditions => ["cats.name = ?", @chloe.name], :limit => 1, :offset => 1)
|
||||
assert_equal 2, @spot.protectors.find(:all, :limit => 100, :offset => 1).size
|
||||
end
|
||||
|
||||
def test_single_custom_finder_parameters_carry_to_individual_relationships
|
||||
# XXX test nullout here
|
||||
end
|
||||
|
||||
def test_double_custom_finder_parameters_carry_to_individual_relationships
|
||||
# XXX test nullout here
|
||||
end
|
||||
|
||||
def test_include_doesnt_fail
|
||||
assert_nothing_raised do
|
||||
@spot.protectors.find(:all, :include => :wild_boars)
|
||||
end
|
||||
end
|
||||
|
||||
def test_abstract_method
|
||||
assert_equal :correct_abstract_method_response, @spot.an_abstract_method
|
||||
end
|
||||
|
||||
def test_missing_target_should_raise
|
||||
@kibbles.eaters << [@kibbles, @alice, @puma, @spot, @bits]
|
||||
@spot.destroy_without_callbacks
|
||||
assert_raises(@association_error) { @kibbles.eaters.reload }
|
||||
# assert_raises(@association_error) { @kibbles.eater_dogs.reload } # bah AR
|
||||
end
|
||||
|
||||
def test_lazy_loading_is_lazy
|
||||
# XXX
|
||||
end
|
||||
|
||||
def test_push_with_skip_duplicates_false_doesnt_load_target
|
||||
# XXX
|
||||
end
|
||||
|
||||
def test_association_foreign_key_is_sane
|
||||
assert_equal "eater_id", Petfood.reflect_on_association(:eaters).association_foreign_key
|
||||
end
|
||||
|
||||
def test_reflection_instance_methods_are_sane
|
||||
assert_equal EatersFoodstuff, Petfood.reflect_on_association(:eaters).klass
|
||||
assert_equal EatersFoodstuff.name, Petfood.reflect_on_association(:eaters).class_name
|
||||
end
|
||||
|
||||
def test_parent_order_orders_parents
|
||||
@alice.foodstuffs_of_eaters << Petfood.find(:all, :order => "the_petfood_primary_key ASC")
|
||||
@alice.reload #not necessary
|
||||
assert_equal [2,1], @alice.foodstuffs_of_eaters.map(&:id)
|
||||
end
|
||||
|
||||
# def test_polymorphic_include
|
||||
# @kibbles.eaters << [@kibbles, @alice, @puma, @spot, @bits]
|
||||
# assert @kibbles.eaters.include?(@kibbles.eaters_foodstuffs.find(:all, :include => :eater).first.eater)
|
||||
# end
|
||||
#
|
||||
# def test_double_polymorphic_include
|
||||
# end
|
||||
#
|
||||
# def test_single_child_include
|
||||
# end
|
||||
#
|
||||
# def test_double_child_include
|
||||
# end
|
||||
#
|
||||
# def test_single_include_from_parent
|
||||
# end
|
||||
#
|
||||
# def test_double_include_from_parent
|
||||
# end
|
||||
#
|
||||
# def test_meta_referential_single_include
|
||||
# end
|
||||
#
|
||||
# def test_meta_referential_double_include
|
||||
# end
|
||||
#
|
||||
# def test_meta_referential_single_include
|
||||
# end
|
||||
#
|
||||
# def test_meta_referential_single_double_multi_include
|
||||
# end
|
||||
#
|
||||
# def test_dont_ignore_duplicates
|
||||
# end
|
||||
#
|
||||
# def test_ignore_duplicates
|
||||
# end
|
||||
#
|
||||
# def test_tagging_system_generator
|
||||
# end
|
||||
#
|
||||
# def test_tagging_system_library
|
||||
# end
|
||||
|
||||
end
|
||||
|
|
|
|||
20
tracks/vendor/plugins/rails_rcov/MIT-LICENSE
vendored
Normal file
20
tracks/vendor/plugins/rails_rcov/MIT-LICENSE
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2006 Coda Hale
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
86
tracks/vendor/plugins/rails_rcov/README
vendored
Normal file
86
tracks/vendor/plugins/rails_rcov/README
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
= rails_rcov plugin for Rails
|
||||
|
||||
rails_rcov provides easy-to-use Rake tasks to determine the code coverage of
|
||||
your unit, functional, and integration tests using Mauricio Fernandez's rcov
|
||||
tool.
|
||||
|
||||
== Installation
|
||||
|
||||
First, install rcov from Mauricio's web site
|
||||
[http://eigenclass.org/hiki.rb?rcov]. Make sure it's on your system path, so
|
||||
that typing +rcov+ on the command line actually runs it. THIS PLUGIN DOESN'T DO
|
||||
ANYTHING BESIDES GENERATE ERRORS UNLESS YOU INSTALL RCOV FIRST. RCOV CONTAINS
|
||||
ALL THE MAGIC, THIS PLUGIN JUST RUNS IT.
|
||||
|
||||
Second, install this plugin. If your project is source-controlled by Subversion
|
||||
(which it should be, really), the easiest way to install this is via Rails'
|
||||
plugin script:
|
||||
|
||||
./script/plugin install -x http://svn.codahale.com/rails_rcov
|
||||
|
||||
If you're not using Subversion, or if you don't want it adding
|
||||
<tt>svn:externals</tt> in your project, remove the <tt>-x</tt> switch:
|
||||
|
||||
./script/plugin install http://svn.codahale.com/rails_rcov
|
||||
|
||||
== Usage
|
||||
|
||||
For each <tt>test:blah</tt> task you have for your Rails project, rails_rcov
|
||||
adds two more: <tt>test:blah:rcov</tt> and <tt>test:blah:clobber_rcov</tt>.
|
||||
|
||||
Running <tt>rake test:units:rcov</tt>, for example, will run your unit tests
|
||||
through rcov and write the code coverage reports to
|
||||
<tt>your_rails_app/coverage/units</tt>.
|
||||
|
||||
Running <tt>test:units:clobber_rcov</tt> will erase the generated report for the
|
||||
unit tests.
|
||||
|
||||
Each rcov task takes two optional parameters: RCOV_PARAMS, whose argument is
|
||||
passed along to rcov, and SHOW_ONLY, which limits the files displayed in the
|
||||
report.
|
||||
|
||||
RCOV_PARAMS:
|
||||
# sort by coverage
|
||||
rake test:units:rcov RCOV_PARAMS="--sort=coverage"
|
||||
|
||||
# show callsites and hide fully covered files
|
||||
rake test:units:rcov RCOV_PARAMS="--callsites --only-uncovered"
|
||||
|
||||
Check the rcov documentation for more details.
|
||||
|
||||
SHOW_ONLY is a comma-separated list of the files you'd like to see. Right now
|
||||
there are four types of files rake_rcov recognizes: models, helpers,
|
||||
controllers, and lib. These can be abbreviated to their first letters:
|
||||
|
||||
# only show files from app/models
|
||||
rake test:units:rcov SHOW_ONLY=models
|
||||
|
||||
# only show files from app/helpers and app/controllers
|
||||
rake test:units:rcov SHOW_ONLY=helpers,controllers
|
||||
|
||||
# only show files from app/helpers and app/controllers, with less typing
|
||||
rake test:units:rcov SHOW_ONLY=h,c
|
||||
|
||||
Please note that rails_rcov has only been tested with a Bash shell, and any
|
||||
other environment could well explode in your face. If you're having trouble
|
||||
getting this to work on Windows, please take the time to figure out what's not
|
||||
working. Most of the time it boils down to the different ways the Window shell
|
||||
and the Bash shell escape metacharacters. Play around with the way rcov_rake
|
||||
escapes data (like on line 73, or 78) and send me a fix. I don't have a working
|
||||
Windows environment anymore, so leaving it up to me won't solve anything. ;-)
|
||||
|
||||
== Resources
|
||||
|
||||
=== Subversion
|
||||
|
||||
* http://svn.codahale.com/rails_rcov
|
||||
|
||||
=== Blog
|
||||
|
||||
* http://blog.codahale.com
|
||||
|
||||
== Credits
|
||||
|
||||
Written by Coda Hale <coda.hale@gmail.com>. Thanks to Nils Franzen for a Win32
|
||||
escaping patch. Thanks to Alex Wayne for suggesting how to make SHOW_ONLY not be
|
||||
useless.
|
||||
150
tracks/vendor/plugins/rails_rcov/tasks/rails_rcov.rake
vendored
Normal file
150
tracks/vendor/plugins/rails_rcov/tasks/rails_rcov.rake
vendored
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
# This File Uses Magic
|
||||
# ====================
|
||||
# Here's an example of how this file works. As an example, let's say you typed
|
||||
# this into your terminal:
|
||||
#
|
||||
# $ rake --tasks
|
||||
#
|
||||
# The rake executable goes through all the various places .rake files can be,
|
||||
# accumulates them all, and then runs them. When this file is loaded by Rake,
|
||||
# it iterates through all the tasks, and for each task named 'test:blah' adds
|
||||
# test:blah:rcov and test:blah:rcov_clobber.
|
||||
#
|
||||
# So you've seen all the tasks, and you type this into your terminal:
|
||||
#
|
||||
# $ rake test:units:rcov
|
||||
#
|
||||
# Rake does the same thing as above, but it runs the test:units:rcov task, which
|
||||
# pretty much just does this:
|
||||
#
|
||||
# $ ruby [this file] [the test you want to run] [some options]
|
||||
#
|
||||
# Now this file is run via the Ruby interpreter, and after glomming up the
|
||||
# options, it acts just like the Rake executable, with a slight difference: it
|
||||
# passes all the arguments to rcov, not ruby, so all your unit tests get some
|
||||
# rcov sweet loving.
|
||||
|
||||
if ARGV.grep(/--run-rake-task=/).empty?
|
||||
# Define all our Rake tasks
|
||||
|
||||
require 'rake/clean'
|
||||
require 'rcov/rcovtask'
|
||||
|
||||
def to_rcov_task_sym(s)
|
||||
s = s.gsub(/(test:)/,'')
|
||||
s.empty? ? nil : s.intern
|
||||
end
|
||||
|
||||
def to_rcov_task_name(s)
|
||||
s = s.gsub(/(test:)/,'')
|
||||
s =~ /s$/i ? s[0..-2] : s
|
||||
end
|
||||
|
||||
def new_rcov_task(test_name)
|
||||
output_dir = "./coverage/#{test_name.gsub('test:','')}"
|
||||
CLOBBER.include(output_dir)
|
||||
|
||||
# Add a task to run the rcov process
|
||||
desc "Run all #{to_rcov_task_name(test_name)} tests with Rcov to measure coverage"
|
||||
task :rcov => [:clobber_rcov] do |t|
|
||||
run_code = '"' << File.expand_path(__FILE__) << '"'
|
||||
run_code << " --run-rake-task=#{test_name}"
|
||||
|
||||
params = String.new
|
||||
if ENV['RCOV_PARAMS']
|
||||
params << ENV['RCOV_PARAMS']
|
||||
end
|
||||
|
||||
# rake test:units:rcov SHOW_ONLY=models,controllers,lib,helpers
|
||||
# rake test:units:rcov SHOW_ONLY=m,c,l,h
|
||||
if ENV['SHOW_ONLY']
|
||||
show_only = ENV['SHOW_ONLY'].to_s.split(',').map{|x|x.strip}
|
||||
if show_only.any?
|
||||
reg_exp = []
|
||||
for show_type in show_only
|
||||
reg_exp << case show_type
|
||||
when 'm', 'models' : 'app\/models'
|
||||
when 'c', 'controllers' : 'app\/controllers'
|
||||
when 'h', 'helpers' : 'app\/helpers'
|
||||
when 'l', 'lib' : 'lib'
|
||||
else
|
||||
show_type
|
||||
end
|
||||
end
|
||||
reg_exp.map!{ |m| "(#{m})" }
|
||||
params << " -x \\\"^(?!#{reg_exp.join('|')})\\\""
|
||||
end
|
||||
end
|
||||
|
||||
unless params.empty?
|
||||
run_code << " --rcov-params=\"#{params}\""
|
||||
end
|
||||
|
||||
ruby run_code
|
||||
end
|
||||
|
||||
# Add a task to clean up after ourselves
|
||||
desc "Remove Rcov reports for #{to_rcov_task_name(test_name)} tests"
|
||||
task :clobber_rcov do |t|
|
||||
rm_r output_dir, :force => true
|
||||
end
|
||||
|
||||
# Link our clobber task to the main one
|
||||
task :clobber => [:clobber_rcov]
|
||||
end
|
||||
|
||||
test_tasks = Rake::Task.tasks.select{ |t| t.comment && t.name =~ /^test/ }
|
||||
for test_task in test_tasks
|
||||
namespace :test do
|
||||
if sym = to_rcov_task_sym(test_task.name)
|
||||
namespace sym do
|
||||
new_rcov_task(test_task.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
# Load rake tasks, hijack ruby, and redirect the task through rcov
|
||||
require 'rubygems'
|
||||
require 'rake'
|
||||
|
||||
module RcovTestSettings
|
||||
class << self
|
||||
attr_accessor :output_dir, :options
|
||||
def to_params
|
||||
"-o \"#{@output_dir}\" -T -x \"rubygems/*,rcov*\" --rails #{@options}"
|
||||
end
|
||||
end
|
||||
|
||||
# load options and arguments from command line
|
||||
unless (cmd_line = ARGV.grep(/--rcov-params=/)).empty?
|
||||
@options = cmd_line.first.gsub(/--rcov-params=/, '')
|
||||
end
|
||||
end
|
||||
|
||||
def is_windows?
|
||||
processor, platform, *rest = RUBY_PLATFORM.split("-")
|
||||
platform == 'mswin32'
|
||||
end
|
||||
|
||||
# intercept what Rake *would* be doing with Ruby, and send it through Rcov instead
|
||||
module RakeFileUtils
|
||||
alias :ruby_without_rcov :ruby
|
||||
def ruby(*args, &block)
|
||||
cmd = (is_windows? ? 'rcov.cmd' : 'rcov') << " #{RcovTestSettings.to_params} #{args}"
|
||||
status = sh(cmd, {}, &block)
|
||||
puts "View the full results at <file://#{RcovTestSettings.output_dir}/index.html>"
|
||||
return status
|
||||
end
|
||||
end
|
||||
|
||||
# read the test name and execute it (through Rcov)
|
||||
unless (cmd_line = ARGV.grep(/--run-rake-task=/)).empty?
|
||||
test_name = cmd_line.first.gsub(/--run-rake-task=/,'')
|
||||
ARGV.clear; ARGV << test_name
|
||||
RcovTestSettings.output_dir = File.expand_path("./coverage/#{test_name.gsub('test:','')}")
|
||||
Rake.application.run
|
||||
else
|
||||
raise "No test to execute!"
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue