PageRenderTime 65ms CodeModel.GetById 34ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 0ms

/activesupport/test/core_ext/module_test.rb

https://github.com/cjolly/rails
Ruby | 510 lines | 397 code | 107 blank | 6 comment | 4 complexity | 19c919451c57b4e542264c82cdd13195 MD5 | raw file
  1# frozen_string_literal: true
  2
  3require "abstract_unit"
  4require "active_support/core_ext/module"
  5
  6Somewhere = Struct.new(:street, :city) do
  7  attr_accessor :name
  8end
  9
 10Someone = Struct.new(:name, :place) do
 11  delegate :street, :city, :to_f, to: :place
 12  delegate :name=, to: :place, prefix: true
 13  delegate :upcase, to: "place.city"
 14  delegate :table_name, to: :class
 15  delegate :table_name, to: :class, prefix: true
 16
 17  def self.table_name
 18    "some_table"
 19  end
 20
 21  self::FAILED_DELEGATE_LINE = __LINE__ + 1
 22  delegate :foo, to: :place
 23
 24  self::FAILED_DELEGATE_LINE_2 = __LINE__ + 1
 25  delegate :bar, to: :place, allow_nil: true
 26
 27  private
 28
 29    def private_name
 30      "Private"
 31    end
 32end
 33
 34Invoice = Struct.new(:client) do
 35  delegate :street, :city, :name, to: :client, prefix: true
 36  delegate :street, :city, :name, to: :client, prefix: :customer
 37end
 38
 39Project = Struct.new(:description, :person) do
 40  delegate :name, to: :person, allow_nil: true
 41  delegate :to_f, to: :description, allow_nil: true
 42end
 43
 44Developer = Struct.new(:client) do
 45  delegate :name, to: :client, prefix: nil
 46end
 47
 48Event = Struct.new(:case) do
 49  delegate :foo, to: :case
 50end
 51
 52Tester = Struct.new(:client) do
 53  delegate :name, to: :client, prefix: false
 54
 55  def foo; 1; end
 56end
 57
 58Product = Struct.new(:name) do
 59  delegate :name, to: :manufacturer, prefix: true
 60  delegate :name, to: :type, prefix: true
 61
 62  def manufacturer
 63    @manufacturer ||= begin
 64      nil.unknown_method
 65    end
 66  end
 67
 68  def type
 69    @type ||= begin
 70      nil.type_name
 71    end
 72  end
 73end
 74
 75module ExtraMissing
 76  def method_missing(sym, *args)
 77    if sym == :extra_missing
 78      42
 79    else
 80      super
 81    end
 82  end
 83
 84  def respond_to_missing?(sym, priv = false)
 85    sym == :extra_missing || super
 86  end
 87end
 88
 89DecoratedTester = Struct.new(:client) do
 90  include ExtraMissing
 91
 92  delegate_missing_to :client
 93end
 94
 95class DecoratedReserved
 96  delegate_missing_to :case
 97
 98  attr_reader :case
 99
100  def initialize(kase)
101    @case = kase
102  end
103end
104
105class Block
106  def hello?
107    true
108  end
109end
110
111HasBlock = Struct.new(:block) do
112  delegate :hello?, to: :block
113end
114
115class ParameterSet
116  delegate :[], :[]=, to: :@params
117
118  def initialize
119    @params = { foo: "bar" }
120  end
121end
122
123class Name
124  delegate :upcase, to: :@full_name
125
126  def initialize(first, last)
127    @full_name = "#{first} #{last}"
128  end
129end
130
131class SideEffect
132  attr_reader :ints
133
134  delegate :to_i, to: :shift, allow_nil: true
135  delegate :to_s, to: :shift
136
137  def initialize
138    @ints = [1, 2, 3]
139  end
140
141  def shift
142    @ints.shift
143  end
144end
145
146class ModuleTest < ActiveSupport::TestCase
147  def setup
148    @david = Someone.new("David", Somewhere.new("Paulina", "Chicago"))
149  end
150
151  def test_delegation_to_methods
152    assert_equal "Paulina", @david.street
153    assert_equal "Chicago", @david.city
154  end
155
156  def test_delegation_to_assignment_method
157    @david.place_name = "Fred"
158    assert_equal "Fred", @david.place.name
159  end
160
161  def test_delegation_to_index_get_method
162    @params = ParameterSet.new
163    assert_equal "bar", @params[:foo]
164  end
165
166  def test_delegation_to_index_set_method
167    @params = ParameterSet.new
168    @params[:foo] = "baz"
169    assert_equal "baz", @params[:foo]
170  end
171
172  def test_delegation_down_hierarchy
173    assert_equal "CHICAGO", @david.upcase
174  end
175
176  def test_delegation_to_instance_variable
177    david = Name.new("David", "Hansson")
178    assert_equal "DAVID HANSSON", david.upcase
179  end
180
181  def test_delegation_to_class_method
182    assert_equal "some_table", @david.table_name
183    assert_equal "some_table", @david.class_table_name
184  end
185
186  def test_missing_delegation_target
187    assert_raise(ArgumentError) do
188      Name.send :delegate, :nowhere
189    end
190    assert_raise(ArgumentError) do
191      Name.send :delegate, :noplace, tos: :hollywood
192    end
193  end
194
195  def test_delegation_target_when_prefix_is_true
196    assert_nothing_raised do
197      Name.send :delegate, :go, to: :you, prefix: true
198    end
199    assert_nothing_raised do
200      Name.send :delegate, :go, to: :_you, prefix: true
201    end
202    assert_raise(ArgumentError) do
203      Name.send :delegate, :go, to: :You, prefix: true
204    end
205    assert_raise(ArgumentError) do
206      Name.send :delegate, :go, to: :@you, prefix: true
207    end
208  end
209
210  def test_delegation_prefix
211    invoice = Invoice.new(@david)
212    assert_equal "David", invoice.client_name
213    assert_equal "Paulina", invoice.client_street
214    assert_equal "Chicago", invoice.client_city
215  end
216
217  def test_delegation_custom_prefix
218    invoice = Invoice.new(@david)
219    assert_equal "David", invoice.customer_name
220    assert_equal "Paulina", invoice.customer_street
221    assert_equal "Chicago", invoice.customer_city
222  end
223
224  def test_delegation_prefix_with_nil_or_false
225    assert_equal "David", Developer.new(@david).name
226    assert_equal "David", Tester.new(@david).name
227  end
228
229  def test_delegation_prefix_with_instance_variable
230    assert_raise ArgumentError do
231      Class.new do
232        def initialize(client)
233          @client = client
234        end
235        delegate :name, :address, to: :@client, prefix: true
236      end
237    end
238  end
239
240  def test_delegation_with_allow_nil
241    rails = Project.new("Rails", Someone.new("David"))
242    assert_equal "David", rails.name
243  end
244
245  def test_delegation_with_allow_nil_and_nil_value
246    rails = Project.new("Rails")
247    assert_nil rails.name
248  end
249
250  # Ensures with check for nil, not for a falseish target.
251  def test_delegation_with_allow_nil_and_false_value
252    project = Project.new(false, false)
253    assert_raise(NoMethodError) { project.name }
254  end
255
256  def test_delegation_with_allow_nil_and_invalid_value
257    rails = Project.new("Rails", "David")
258    assert_raise(NoMethodError) { rails.name }
259  end
260
261  def test_delegation_with_allow_nil_and_nil_value_and_prefix
262    Project.class_eval do
263      delegate :name, to: :person, allow_nil: true, prefix: true
264    end
265    rails = Project.new("Rails")
266    assert_nil rails.person_name
267  end
268
269  def test_delegation_without_allow_nil_and_nil_value
270    david = Someone.new("David")
271    assert_raise(Module::DelegationError) { david.street }
272  end
273
274  def test_delegation_to_method_that_exists_on_nil
275    nil_person = Someone.new(nil)
276    assert_equal 0.0, nil_person.to_f
277  end
278
279  def test_delegation_to_method_that_exists_on_nil_when_allowing_nil
280    nil_project = Project.new(nil)
281    assert_equal 0.0, nil_project.to_f
282  end
283
284  def test_delegation_does_not_raise_error_when_removing_singleton_instance_methods
285    parent = Class.new do
286      def self.parent_method; end
287    end
288
289    assert_nothing_raised do
290      Class.new(parent) do
291        class << self
292          delegate :parent_method, to: :superclass
293        end
294      end
295    end
296  end
297
298  def test_delegation_line_number
299    _, line = Someone.instance_method(:foo).source_location
300    assert_equal Someone::FAILED_DELEGATE_LINE, line
301  end
302
303  def test_delegate_line_with_nil
304    _, line = Someone.instance_method(:bar).source_location
305    assert_equal Someone::FAILED_DELEGATE_LINE_2, line
306  end
307
308  def test_delegation_exception_backtrace
309    someone = Someone.new("foo", "bar")
310    someone.foo
311  rescue NoMethodError => e
312    file_and_line = "#{__FILE__}:#{Someone::FAILED_DELEGATE_LINE}"
313    # We can't simply check the first line of the backtrace, because JRuby reports the call to __send__ in the backtrace.
314    assert e.backtrace.any? { |a| a.include?(file_and_line) },
315           "[#{e.backtrace.inspect}] did not include [#{file_and_line}]"
316  end
317
318  def test_delegation_exception_backtrace_with_allow_nil
319    someone = Someone.new("foo", "bar")
320    someone.bar
321  rescue NoMethodError => e
322    file_and_line = "#{__FILE__}:#{Someone::FAILED_DELEGATE_LINE_2}"
323    # We can't simply check the first line of the backtrace, because JRuby reports the call to __send__ in the backtrace.
324    assert e.backtrace.any? { |a| a.include?(file_and_line) },
325           "[#{e.backtrace.inspect}] did not include [#{file_and_line}]"
326  end
327
328  def test_delegation_invokes_the_target_exactly_once
329    se = SideEffect.new
330
331    assert_equal 1, se.to_i
332    assert_equal [2, 3], se.ints
333
334    assert_equal "2", se.to_s
335    assert_equal [3], se.ints
336  end
337
338  def test_delegation_doesnt_mask_nested_no_method_error_on_nil_receiver
339    product = Product.new("Widget")
340
341    # Nested NoMethodError is a different name from the delegation
342    assert_raise(NoMethodError) { product.manufacturer_name }
343
344    # Nested NoMethodError is the same name as the delegation
345    assert_raise(NoMethodError) { product.type_name }
346  end
347
348  def test_delegation_with_method_arguments
349    has_block = HasBlock.new(Block.new)
350    assert_predicate has_block, :hello?
351  end
352
353  def test_delegate_missing_to_with_method
354    assert_equal "David", DecoratedTester.new(@david).name
355  end
356
357  def test_delegate_missing_to_with_reserved_methods
358    assert_equal "David", DecoratedReserved.new(@david).name
359  end
360
361  def test_delegate_missing_to_does_not_delegate_to_private_methods
362    e = assert_raises(NoMethodError) do
363      DecoratedReserved.new(@david).private_name
364    end
365
366    assert_match(/undefined method `private_name' for/, e.message)
367  end
368
369  def test_delegate_missing_to_does_not_delegate_to_fake_methods
370    e = assert_raises(NoMethodError) do
371      DecoratedReserved.new(@david).my_fake_method
372    end
373
374    assert_match(/undefined method `my_fake_method' for/, e.message)
375  end
376
377  def test_delegate_missing_to_raises_delegation_error_if_target_nil
378    e = assert_raises(Module::DelegationError) do
379      DecoratedTester.new(nil).name
380    end
381
382    assert_equal "name delegated to client, but client is nil", e.message
383  end
384
385  def test_delegate_missing_to_affects_respond_to
386    assert_respond_to DecoratedTester.new(@david), :name
387    assert_not_respond_to DecoratedTester.new(@david), :private_name
388    assert_not_respond_to DecoratedTester.new(@david), :my_fake_method
389
390    assert DecoratedTester.new(@david).respond_to?(:name, true)
391    assert_not DecoratedTester.new(@david).respond_to?(:private_name, true)
392    assert_not DecoratedTester.new(@david).respond_to?(:my_fake_method, true)
393  end
394
395  def test_delegate_missing_to_respects_superclass_missing
396    assert_equal 42, DecoratedTester.new(@david).extra_missing
397
398    assert_respond_to DecoratedTester.new(@david), :extra_missing
399  end
400
401  def test_delegate_with_case
402    event = Event.new(Tester.new)
403    assert_equal 1, event.foo
404  end
405
406  def test_private_delegate
407    location = Class.new do
408      def initialize(place)
409        @place = place
410      end
411
412      private(*delegate(:street, :city, to: :@place))
413    end
414
415    place = location.new(Somewhere.new("Such street", "Sad city"))
416
417    assert_not_respond_to place, :street
418    assert_not_respond_to place, :city
419
420    assert place.respond_to?(:street, true) # Asking for private method
421    assert place.respond_to?(:city, true)
422  end
423
424  def test_private_delegate_prefixed
425    location = Class.new do
426      def initialize(place)
427        @place = place
428      end
429
430      private(*delegate(:street, :city, to: :@place, prefix: :the))
431    end
432
433    place = location.new(Somewhere.new("Such street", "Sad city"))
434
435    assert_not_respond_to place, :street
436    assert_not_respond_to place, :city
437
438    assert_not_respond_to place, :the_street
439    assert place.respond_to?(:the_street, true)
440    assert_not_respond_to place, :the_city
441    assert place.respond_to?(:the_city, true)
442  end
443
444  def test_private_delegate_with_private_option
445    location = Class.new do
446      def initialize(place)
447        @place = place
448      end
449
450      delegate(:street, :city, to: :@place, private: true)
451    end
452
453    place = location.new(Somewhere.new("Such street", "Sad city"))
454
455    assert_not_respond_to place, :street
456    assert_not_respond_to place, :city
457
458    assert place.respond_to?(:street, true) # Asking for private method
459    assert place.respond_to?(:city, true)
460  end
461
462  def test_some_public_some_private_delegate_with_private_option
463    location = Class.new do
464      def initialize(place)
465        @place = place
466      end
467
468      delegate(:street, to: :@place)
469      delegate(:city, to: :@place, private: true)
470    end
471
472    place = location.new(Somewhere.new("Such street", "Sad city"))
473
474    assert_respond_to place, :street
475    assert_not_respond_to place, :city
476
477    assert place.respond_to?(:city, true) # Asking for private method
478  end
479
480  def test_private_delegate_prefixed_with_private_option
481    location = Class.new do
482      def initialize(place)
483        @place = place
484      end
485
486      delegate(:street, :city, to: :@place, prefix: :the, private: true)
487    end
488
489    place = location.new(Somewhere.new("Such street", "Sad city"))
490
491    assert_not_respond_to place, :the_street
492    assert place.respond_to?(:the_street, true)
493    assert_not_respond_to place, :the_city
494    assert place.respond_to?(:the_city, true)
495  end
496
497  def test_delegate_with_private_option_returns_names_of_delegate_methods
498    location = Class.new do
499      def initialize(place)
500        @place = place
501      end
502    end
503
504    assert_equal [:street, :city],
505      location.delegate(:street, :city, to: :@place, private: true)
506
507    assert_equal [:the_street, :the_city],
508      location.delegate(:street, :city, to: :@place, prefix: :the, private: true)
509  end
510end