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