Skip to content

Commit

Permalink
Merge branch 'master' into mail
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremy committed Nov 23, 2009
2 parents 671538c + f8d06e6 commit ada895e
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 102 deletions.
3 changes: 0 additions & 3 deletions Gemfile
@@ -1,6 +1,3 @@
clear_sources
source 'http://gemcutter.org'

gem "rake", ">= 0.8.7"
gem "mocha", ">= 0.9.8"

Expand Down
14 changes: 6 additions & 8 deletions actionpack/test/controller/routing_test.rb
Expand Up @@ -1419,15 +1419,13 @@ def test_route_requirement_generate_with_ignore_case
:requirements => {:name => /(david|jamis)/i}
end

pending do
url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'})
assert_equal "/page/david", url
assert_raise ActionController::RoutingError do
url = set.generate({:controller => 'pages', :action => 'show', :name => 'davidjamis'})
end
url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'})
assert_equal "/page/JAMIS", url
url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'})
assert_equal "/page/david", url
assert_raise ActionController::RoutingError do
url = set.generate({:controller => 'pages', :action => 'show', :name => 'davidjamis'})
end
url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'})
assert_equal "/page/JAMIS", url
end

def test_route_requirement_recognize_with_extended_syntax
Expand Down
10 changes: 9 additions & 1 deletion activerecord/lib/active_record/associations.rb
Expand Up @@ -61,6 +61,12 @@ def initialize(owner, reflection)
end
end

class HasAndBelongsToManyAssociationWithPrimaryKeyError < ActiveRecordError #:nodoc:
def initialize(reflection)
super("Primary key is not allowed in a has_and_belongs_to_many join table (#{reflection.options[:join_table]}).")
end
end

class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc:
def initialize(reflection)
super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.")
Expand Down Expand Up @@ -1675,7 +1681,6 @@ def create_belongs_to_reflection(association_id, options)

def create_has_and_belongs_to_many_reflection(association_id, options, &extension)
options.assert_valid_keys(valid_keys_for_has_and_belongs_to_many_association)

options[:extend] = create_extension_modules(association_id, extension, options[:extend])

reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self)
Expand All @@ -1685,6 +1690,9 @@ def create_has_and_belongs_to_many_reflection(association_id, options, &extensio
end

reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name))
if connection.supports_primary_key? && (connection.primary_key(reflection.options[:join_table]) rescue false)
raise HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection)
end

reflection
end
Expand Down
@@ -1,11 +1,6 @@
module ActiveRecord
module Associations
class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
def initialize(owner, reflection)
super
@primary_key_list = {}
end

def create(attributes = {})
create_record(attributes) { |record| insert_record(record) }
end
Expand All @@ -23,9 +18,7 @@ def reset_column_information
end

def has_primary_key?
return @has_primary_key unless @has_primary_key.nil?
@has_primary_key = (@owner.connection.supports_primary_key? &&
@owner.connection.primary_key(@reflection.options[:join_table]))
@has_primary_key ||= @owner.connection.supports_primary_key? && @owner.connection.primary_key(@reflection.options[:join_table])
end

protected
Expand All @@ -40,11 +33,6 @@ def count_records
end

def insert_record(record, force = true, validate = true)
if has_primary_key?
raise ActiveRecord::ConfigurationError,
"Primary key is not allowed in a has_and_belongs_to_many join table (#{@reflection.options[:join_table]})."
end

if record.new_record?
if force
record.save!
Expand Down
16 changes: 2 additions & 14 deletions activerecord/test/cases/associations/habtm_join_table_test.rb
Expand Up @@ -36,21 +36,9 @@ def teardown
uses_transaction :test_should_raise_exception_when_join_table_has_a_primary_key
def test_should_raise_exception_when_join_table_has_a_primary_key
if ActiveRecord::Base.connection.supports_primary_key?
assert_raise ActiveRecord::ConfigurationError do
jaime = MyReader.create(:name=>"Jaime")
jaime.my_books << MyBook.create(:name=>'Great Expectations')
assert_raise ActiveRecord::HasAndBelongsToManyAssociationWithPrimaryKeyError do
MyReader.has_and_belongs_to_many :my_books
end
end
end

uses_transaction :test_should_cache_result_of_primary_key_check
def test_should_cache_result_of_primary_key_check
if ActiveRecord::Base.connection.supports_primary_key?
ActiveRecord::Base.connection.stubs(:primary_key).with('my_books_my_readers').returns(false).once
weaz = MyReader.create(:name=>'Weaz')

weaz.my_books << MyBook.create(:name=>'Great Expectations')
weaz.my_books << MyBook.create(:name=>'Greater Expectations')
end
end
end
3 changes: 1 addition & 2 deletions activerecord/test/fixtures/edges.yml
@@ -1,6 +1,5 @@
<% (1..4).each do |id| %>
edge_<%= id %>:
id: <%= id %>
source_id: <%= id %>
sink_id: <%= id + 1 %>
<% end %>
<% end %>
2 changes: 1 addition & 1 deletion activerecord/test/schema/schema.rb
Expand Up @@ -160,7 +160,7 @@ def create_table(*args, &block)
t.integer :access_level, :default => 1
end

create_table :edges, :force => true do |t|
create_table :edges, :force => true, :id => false do |t|
t.column :source_id, :integer, :null => false
t.column :sink_id, :integer, :null => false
end
Expand Down
4 changes: 2 additions & 2 deletions activesupport/lib/active_support/core_ext/logger.rb
Expand Up @@ -137,10 +137,10 @@ def format_message(severity, timestamp, msg, progname)
attr_writer :formatter
public :formatter=

alias old_format_datetime format_datetime
alias old_format_datetime format_datetime if method_defined?(:format_datetime)
def format_datetime(datetime) datetime end

alias old_msg2str msg2str
alias old_msg2str msg2str if method_defined?(:msg2str)
def msg2str(msg) msg end
end
end
92 changes: 58 additions & 34 deletions activesupport/lib/active_support/notifications.rb
Expand Up @@ -41,7 +41,7 @@ module ActiveSupport
# to subscribers in a thread. You can use any queue implementation you want.
#
module Notifications
mattr_accessor :queue
mattr_accessor :queue, :listener

class << self
delegate :instrument, :transaction_id, :transaction, :to => :instrumenter
Expand All @@ -54,8 +54,13 @@ def publisher
@publisher ||= Publisher.new(queue)
end

def subscribe(pattern=nil, &block)
Subscriber.new(queue).bind(pattern).subscribe(&block)
def subscriber
@subscriber ||= Subscriber.new(queue)
end

def subscribe(pattern=nil, options={}, &block)
with = options[:with] || listener
subscriber.bind(with, pattern).subscribe(&block)
end
end

Expand Down Expand Up @@ -104,13 +109,14 @@ def initialize(queue)
@queue = queue
end

def bind(pattern)
@pattern = pattern
def bind(listener, pattern)
@listener = listener
@pattern = pattern
self
end

def subscribe
@queue.subscribe(@pattern) do |*args|
@queue.subscribe(@listener, @pattern) do |*args|
yield(*args)
end
end
Expand Down Expand Up @@ -138,6 +144,48 @@ def parent_of?(event)
end
end

class AsyncListener
def initialize(pattern, &block)
@pattern = pattern
@subscriber = block
@queue = Queue.new
Thread.new { consume }
end

def publish(name, *args)
if !@pattern || @pattern === name.to_s
@queue << args.unshift(name)
end
end

def consume
while args = @queue.shift
@subscriber.call(*args)
end
end

def drained?
@queue.size.zero?
end
end

class SyncListener
def initialize(pattern, &block)
@pattern = pattern
@subscriber = block
end

def publish(name, *args)
if !@pattern || @pattern === name.to_s
@subscriber.call(*args.unshift(name))
end
end

def drained?
true
end
end

# This is a default queue implementation that ships with Notifications. It
# consumes events in a thread and publish them to all registered subscribers.
#
Expand All @@ -150,40 +198,16 @@ def publish(*args)
@listeners.each { |l| l.publish(*args) }
end

def subscribe(pattern=nil, &block)
@listeners << Listener.new(pattern, &block)
def subscribe(listener, pattern=nil, &block)
@listeners << listener.new(pattern, &block)
end

def drained?
@listeners.all? &:drained?
end

class Listener
def initialize(pattern, &block)
@pattern = pattern
@subscriber = block
@queue = Queue.new
Thread.new { consume }
end

def publish(name, *args)
if !@pattern || @pattern === name.to_s
@queue << args.unshift(name)
end
end

def consume
while args = @queue.shift
@subscriber.call(*args)
end
end

def drained?
@queue.size.zero?
end
end
end
end

Notifications.queue = Notifications::LittleFanout.new
Notifications.queue = Notifications::LittleFanout.new
Notifications.listener = Notifications::AsyncListener
end
17 changes: 16 additions & 1 deletion activesupport/test/notifications_test.rb
Expand Up @@ -176,6 +176,21 @@ def test_subscriber_with_pattern_as_regexp
assert_equal 1, @another.first.result
end

def test_subscriber_allows_sync_listeners
@another = []
ActiveSupport::Notifications.subscribe(/cache/, :with => ActiveSupport::Notifications::SyncListener) do |*args|
@another << ActiveSupport::Notifications::Event.new(*args)
end

Thread.expects(:new).never
ActiveSupport::Notifications.instrument(:something){ 0 }
ActiveSupport::Notifications.instrument(:cache){ 1 }

assert_equal 1, @another.size
assert_equal :cache, @another.first.name
assert_equal 1, @another.first.result
end

def test_with_several_consumers_and_several_events
@another = []
ActiveSupport::Notifications.subscribe do |*args|
Expand All @@ -201,6 +216,6 @@ def test_with_several_consumers_and_several_events

private
def drain
sleep(0.1) until ActiveSupport::Notifications.queue.drained?
sleep(0.05) until ActiveSupport::Notifications.queue.drained?
end
end
2 changes: 2 additions & 0 deletions railties/CHANGELOG
@@ -1,5 +1,7 @@
*Edge*

* Fixed that the debugger wouldn't go into IRB mode because of left-over ARGVs [DHH]

* I18n support for plugins. #2325 [Antonio Tapiador, Sven Fuchs]

* Ruby 1.9: use UTF-8 for default internal and external encodings. [Jeremy Kemper]
Expand Down
34 changes: 11 additions & 23 deletions railties/lib/rails/generators.rb
Expand Up @@ -86,28 +86,16 @@ def self.options #:nodoc:
@options ||= DEFAULT_OPTIONS.dup
end

# We have two scenarios here: when rubygems is loaded and when bundler is
# being used. If rubygems is loaded, we get all generators paths from loaded
# specs. Otherwise we just have to look into vendor/gems/gems.
#
def self.gems_generators_paths
paths = []

if defined?(Gem) && Gem.respond_to?(:loaded_specs)
Gem.loaded_specs.each do |name, spec|
generator_path = File.join(spec.full_gem_path, "lib/generators")
paths << generator_path if File.exist?(generator_path)
end
def self.gems_generators_paths #:nodoc:
return [] unless defined?(Gem) && Gem.respond_to?(:loaded_specs)
Gem.loaded_specs.inject([]) do |paths, (name, spec)|
paths += Dir[File.join(spec.full_gem_path, "lib/{generators,rails_generators}")]
end

paths
end

# Load paths from plugin.
#
def self.plugins_generators_paths
def self.plugins_generators_paths #:nodoc:
return [] unless Rails.root
Dir[File.join(Rails.root, "vendor", "plugins", "*", "lib", "generators")]
Dir[File.join(Rails.root, "vendor", "plugins", "*", "lib", "{generators,rails_generators}")]
end

# Hold configured generators fallbacks. If a plugin developer wants a
Expand Down Expand Up @@ -147,8 +135,8 @@ def self.no_color!
def self.load_paths
@load_paths ||= begin
paths = []
paths << File.join(Rails.root, "lib", "generators") if Rails.root
paths << File.join(Thor::Util.user_home, ".rails", "generators")
paths += Dir[File.join(Rails.root, "lib", "{generators,rails_generators}")] if Rails.root
paths += Dir[File.join(Thor::Util.user_home, ".rails", "{generators,rails_generators}")]
paths += self.plugins_generators_paths
paths += self.gems_generators_paths
paths << File.expand_path(File.join(File.dirname(__FILE__), "generators"))
Expand Down Expand Up @@ -210,7 +198,7 @@ def self.find_by_namespace(name, base=nil, context=nil) #:nodoc:
return klass if klass
end

invoke_fallbacks_for(name, base)
invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
end

# Receives a namespace, arguments and the behavior to invoke the generator.
Expand Down Expand Up @@ -278,13 +266,13 @@ def self.builtin #:nodoc:

# By default, Rails strips the generator namespace to make invocations
# easier. This method generaters the both possibilities names.
def self.generator_names(first, second)
def self.generator_names(first, second) #:nodoc:
[ "#{first}:generators:#{second}", "#{first}:#{second}" ]
end

# Try callbacks for the given base.
#
def self.invoke_fallbacks_for(name, base)
def self.invoke_fallbacks_for(name, base) #:nodoc:
return nil unless base && fallbacks[base.to_sym]
invoked_fallbacks = []

Expand Down

0 comments on commit ada895e

Please sign in to comment.