require 'abstract_unit' require 'active_support/core_ext/module' module One Constant1 = "Hello World" Constant2 = "What's up?" end class Ab include One Constant1 = "Hello World" # Will have different object id than One::Constant1 Constant3 = "Goodbye World" end module Yz module Zy class Cd include One end end end Somewhere = Struct.new(:street, :city) do attr_accessor :name end class Someone < Struct.new(:name, :place) delegate :street, :city, :to_f, :to => :place delegate :name=, :to => :place, :prefix => true delegate :upcase, :to => "place.city" delegate :table_name, :to => :class delegate :table_name, :to => :class, :prefix => true def self.table_name 'some_table' end FAILED_DELEGATE_LINE = __LINE__ + 1 delegate :foo, :to => :place FAILED_DELEGATE_LINE_2 = __LINE__ + 1 delegate :bar, :to => :place, :allow_nil => true end Invoice = Struct.new(:client) do delegate :street, :city, :name, :to => :client, :prefix => true delegate :street, :city, :name, :to => :client, :prefix => :customer end Project = Struct.new(:description, :person) do delegate :name, :to => :person, :allow_nil => true delegate :to_f, :to => :description, :allow_nil => true end Developer = Struct.new(:client) do delegate :name, :to => :client, :prefix => nil end Event = Struct.new(:case) do delegate :foo, :to => :case end Tester = Struct.new(:client) do delegate :name, :to => :client, :prefix => false def foo; 1; end end Product = Struct.new(:name) do delegate :name, :to => :manufacturer, :prefix => true delegate :name, :to => :type, :prefix => true def manufacturer @manufacturer ||= begin nil.unknown_method end end def type @type ||= begin :thing_without_same_method_name_as_delegated.name end end end class ParameterSet delegate :[], :[]=, :to => :@params def initialize @params = {:foo => "bar"} end end class Name delegate :upcase, :to => :@full_name def initialize(first, last) @full_name = "#{first} #{last}" end end class SideEffect attr_reader :ints delegate :to_i, :to => :shift, :allow_nil => true delegate :to_s, :to => :shift def initialize @ints = [1, 2, 3] end def shift @ints.shift end end class ModuleTest < ActiveSupport::TestCase def setup @david = Someone.new("David", Somewhere.new("Paulina", "Chicago")) end def test_delegation_to_methods assert_equal "Paulina", @david.street assert_equal "Chicago", @david.city end def test_delegation_to_assignment_method @david.place_name = "Fred" assert_equal "Fred", @david.place.name end def test_delegation_to_index_get_method @params = ParameterSet.new assert_equal "bar", @params[:foo] end def test_delegation_to_index_set_method @params = ParameterSet.new @params[:foo] = "baz" assert_equal "baz", @params[:foo] end def test_delegation_down_hierarchy assert_equal "CHICAGO", @david.upcase end def test_delegation_to_instance_variable david = Name.new("David", "Hansson") assert_equal "DAVID HANSSON", david.upcase end def test_delegation_to_class_method assert_equal 'some_table', @david.table_name assert_equal 'some_table', @david.class_table_name end def test_missing_delegation_target assert_raise(ArgumentError) do Name.send :delegate, :nowhere end assert_raise(ArgumentError) do Name.send :delegate, :noplace, :tos => :hollywood end end def test_delegation_prefix invoice = Invoice.new(@david) assert_equal invoice.client_name, "David" assert_equal invoice.client_street, "Paulina" assert_equal invoice.client_city, "Chicago" end def test_delegation_custom_prefix invoice = Invoice.new(@david) assert_equal invoice.customer_name, "David" assert_equal invoice.customer_street, "Paulina" assert_equal invoice.customer_city, "Chicago" end def test_delegation_prefix_with_nil_or_false assert_equal Developer.new(@david).name, "David" assert_equal Tester.new(@david).name, "David" end def test_delegation_prefix_with_instance_variable assert_raise ArgumentError do Class.new do def initialize(client) @client = client end delegate :name, :address, :to => :@client, :prefix => true end end end def test_delegation_with_allow_nil rails = Project.new("Rails", Someone.new("David")) assert_equal rails.name, "David" end def test_delegation_with_allow_nil_and_nil_value rails = Project.new("Rails") assert_nil rails.name end # Ensures with check for nil, not for a falseish target. def test_delegation_with_allow_nil_and_false_value project = Project.new(false, false) assert_raise(NoMethodError) { project.name } end def test_delegation_with_allow_nil_and_invalid_value rails = Project.new("Rails", "David") assert_raise(NoMethodError) { rails.name } end def test_delegation_with_allow_nil_and_nil_value_and_prefix Project.class_eval do delegate :name, :to => :person, :allow_nil => true, :prefix => true end rails = Project.new("Rails") assert_nil rails.person_name end def test_delegation_without_allow_nil_and_nil_value david = Someone.new("David") assert_raise(Module::DelegationError) { david.street } end def test_delegation_to_method_that_exists_on_nil nil_person = Someone.new(nil) assert_equal 0.0, nil_person.to_f end def test_delegation_to_method_that_exists_on_nil_when_allowing_nil nil_project = Project.new(nil) assert_equal 0.0, nil_project.to_f end def test_delegation_does_not_raise_error_when_removing_singleton_instance_methods parent = Class.new do def self.parent_method; end end assert_nothing_raised do Class.new(parent) do class << self delegate :parent_method, :to => :superclass end end end end def test_delegation_line_number _, line = Someone.instance_method(:foo).source_location assert_equal Someone::FAILED_DELEGATE_LINE, line end def test_delegate_line_with_nil _, line = Someone.instance_method(:bar).source_location assert_equal Someone::FAILED_DELEGATE_LINE_2, line end def test_delegation_exception_backtrace someone = Someone.new("foo", "bar") someone.foo rescue NoMethodError => e file_and_line = "#{__FILE__}:#{Someone::FAILED_DELEGATE_LINE}" # We can't simply check the first line of the backtrace, because JRuby reports the call to __send__ in the backtrace. assert e.backtrace.any?{|a| a.include?(file_and_line)}, "[#{e.backtrace.inspect}] did not include [#{file_and_line}]" end def test_delegation_exception_backtrace_with_allow_nil someone = Someone.new("foo", "bar") someone.bar rescue NoMethodError => e file_and_line = "#{__FILE__}:#{Someone::FAILED_DELEGATE_LINE_2}" # We can't simply check the first line of the backtrace, because JRuby reports the call to __send__ in the backtrace. assert e.backtrace.any?{|a| a.include?(file_and_line)}, "[#{e.backtrace.inspect}] did not include [#{file_and_line}]" end def test_delegation_invokes_the_target_exactly_once se = SideEffect.new assert_equal 1, se.to_i assert_equal [2, 3], se.ints assert_equal '2', se.to_s assert_equal [3], se.ints end def test_delegation_doesnt_mask_nested_no_method_error_on_nil_receiver product = Product.new('Widget') # Nested NoMethodError is a different name from the delegation assert_raise(NoMethodError) { product.manufacturer_name } # Nested NoMethodError is the same name as the delegation assert_raise(NoMethodError) { product.type_name } end def test_parent assert_equal Yz::Zy, Yz::Zy::Cd.parent assert_equal Yz, Yz::Zy.parent assert_equal Object, Yz.parent end def test_parents assert_equal [Yz::Zy, Yz, Object], Yz::Zy::Cd.parents assert_equal [Yz, Object], Yz::Zy.parents end def test_local_constants assert_equal %w(Constant1 Constant3), Ab.local_constants.sort.map(&:to_s) end end module BarMethodAliaser def self.included(foo_class) foo_class.class_eval do include BarMethods alias_method_chain :bar, :baz end end end module BarMethods def bar_with_baz bar_without_baz << '_with_baz' end def quux_with_baz! quux_without_baz! << '_with_baz' end def quux_with_baz? false end def quux_with_baz=(v) send(:quux_without_baz=, v) << '_with_baz' end def duck_with_orange duck_without_orange << '_with_orange' end end class MethodAliasingTest < ActiveSupport::TestCase def setup Object.const_set :FooClassWithBarMethod, Class.new { def bar() 'bar' end } @instance = FooClassWithBarMethod.new end def teardown Object.instance_eval { remove_const :FooClassWithBarMethod } end def test_alias_method_chain assert @instance.respond_to?(:bar) feature_aliases = [:bar_with_baz, :bar_without_baz] feature_aliases.each do |method| assert !@instance.respond_to?(method) end assert_equal 'bar', @instance.bar FooClassWithBarMethod.class_eval { include BarMethodAliaser } feature_aliases.each do |method| assert_respond_to @instance, method end assert_equal 'bar_with_baz', @instance.bar assert_equal 'bar', @instance.bar_without_baz end def test_alias_method_chain_with_punctuation_method FooClassWithBarMethod.class_eval do def quux!; 'quux' end end assert !@instance.respond_to?(:quux_with_baz!) FooClassWithBarMethod.class_eval do include BarMethodAliaser alias_method_chain :quux!, :baz end assert_respond_to @instance, :quux_with_baz! assert_equal 'quux_with_baz', @instance.quux! assert_equal 'quux', @instance.quux_without_baz! end def test_alias_method_chain_with_same_names_between_predicates_and_bang_methods FooClassWithBarMethod.class_eval do def quux!; 'quux!' end def quux?; true end def quux=(v); 'quux=' end end assert !@instance.respond_to?(:quux_with_baz!) assert !@instance.respond_to?(:quux_with_baz?) assert !@instance.respond_to?(:quux_with_baz=) FooClassWithBarMethod.class_eval { include BarMethodAliaser } assert_respond_to @instance, :quux_with_baz! assert_respond_to @instance, :quux_with_baz? assert_respond_to @instance, :quux_with_baz= FooClassWithBarMethod.alias_method_chain :quux!, :baz assert_equal 'quux!_with_baz', @instance.quux! assert_equal 'quux!', @instance.quux_without_baz! FooClassWithBarMethod.alias_method_chain :quux?, :baz assert_equal false, @instance.quux? assert_equal true, @instance.quux_without_baz? FooClassWithBarMethod.alias_method_chain :quux=, :baz assert_equal 'quux=_with_baz', @instance.send(:quux=, 1234) assert_equal 'quux=', @instance.send(:quux_without_baz=, 1234) end def test_alias_method_chain_with_feature_punctuation FooClassWithBarMethod.class_eval do def quux; 'quux' end def quux?; 'quux?' end include BarMethodAliaser alias_method_chain :quux, :baz! end assert_nothing_raised do assert_equal 'quux_with_baz', @instance.quux_with_baz! end assert_raise(NameError) do FooClassWithBarMethod.alias_method_chain :quux?, :baz! end end def test_alias_method_chain_yields_target_and_punctuation args = nil FooClassWithBarMethod.class_eval do def quux?; end include BarMethods FooClassWithBarMethod.alias_method_chain :quux?, :baz do |target, punctuation| args = [target, punctuation] end end assert_not_nil args assert_equal 'quux', args[0] assert_equal '?', args[1] end def test_alias_method_chain_preserves_private_method_status FooClassWithBarMethod.class_eval do def duck; 'duck' end include BarMethodAliaser private :duck alias_method_chain :duck, :orange end assert_raise NoMethodError do @instance.duck end assert_equal 'duck_with_orange', @instance.instance_eval { duck } assert FooClassWithBarMethod.private_method_defined?(:duck) end def test_alias_method_chain_preserves_protected_method_status FooClassWithBarMethod.class_eval do def duck; 'duck' end include BarMethodAliaser protected :duck alias_method_chain :duck, :orange end assert_raise NoMethodError do @instance.duck end assert_equal 'duck_with_orange', @instance.instance_eval { duck } assert FooClassWithBarMethod.protected_method_defined?(:duck) end def test_alias_method_chain_preserves_public_method_status FooClassWithBarMethod.class_eval do def duck; 'duck' end include BarMethodAliaser public :duck alias_method_chain :duck, :orange end assert_equal 'duck_with_orange', @instance.duck assert FooClassWithBarMethod.public_method_defined?(:duck) end def test_delegate_with_case event = Event.new(Tester.new) assert_equal 1, event.foo end end