Guest User

Untitled

a guest
Feb 21st, 2018
58
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.21 KB | None | 0 0
  1. require 'rubygems'
  2. require 'spec'
  3.  
  4. if not defined? BlankSlate
  5. class BlankSlate
  6. instance_methods.each { |m| undef_method m unless m =~ /^(__|instance_eval)/ }
  7. end
  8. end
  9.  
  10. class RDingus < BlankSlate
  11. def initialize(parent = nil, invocation = nil)
  12. @parent = parent
  13. @invocation = invocation
  14. @invocations = InvocationList.new
  15. @invocation_expectations = InvocationResultHash.new
  16. @children = []
  17. @call_count = 0
  18. end
  19.  
  20. def method_missing(name, *args, &block)
  21. @call_count += 1
  22. invocation = InvocationRecord.new(self, name, args)
  23. @invocations << invocation
  24.  
  25. if @invocation_expectations.defined?(invocation)
  26. result = @invocation_expectations[invocation]
  27. begin
  28. if result._dingus?
  29. result
  30. end
  31. rescue NoMethodError
  32. # We use this kludge, because it's the only way to test for a dingus
  33. # We need to know it's not a dingus before calling other methods
  34. if result.is_a?(Proc)
  35. obj = Object.new
  36. obj.extend(DingusProc)
  37. obj.args = args
  38. obj.method = name
  39. obj.block = block
  40. obj.instance_eval &result
  41. else
  42. result
  43. end
  44. end
  45. else
  46. dingus = RDingus.new(self, invocation)
  47. @children << dingus
  48. @invocation_expectations[invocation] = dingus
  49. end
  50. end
  51.  
  52. def _define(&block)
  53. proxy = DefinitionProxy.new(self, @invocation_expectations)
  54.  
  55. if block
  56. proxy.instance_eval &block
  57. else
  58. proxy
  59. end
  60. end
  61.  
  62. def _calls
  63. @invocations
  64. end
  65.  
  66. def _dingus?
  67. true
  68. end
  69.  
  70. def inspect
  71. "#<RDingus #{__id__}, calls=#{@call_count}>"
  72. end
  73.  
  74. def dup
  75. self
  76. end
  77.  
  78. def clone
  79. dup
  80. end
  81.  
  82. public
  83.  
  84. module DingusProc
  85. attr_accessor :method, :args, :block
  86. end
  87.  
  88. class DefinitionProxy < BlankSlate
  89. def initialize(parent = nil, list = nil)
  90. @parent = parent
  91. @list = list || InvocationResultHash.new
  92. end
  93.  
  94. def method_missing(m, *args, &block)
  95. if block
  96. invocation = InvocationRecord.new(@parent, m, args, block)
  97. @list[invocation] = block
  98. else
  99. if args.empty?
  100. invocation = InvocationRecord.new(@parent, m, args)
  101. dingus = RDingus.new(@parent, invocation)
  102. @list[invocation] = dingus
  103. dingus._define
  104. else
  105. invocation = InvocationRecord.new(@parent, m, [], args.first)
  106. @list[invocation] = args.first
  107. end
  108. end
  109. end
  110.  
  111. def _define(&block)
  112. self.instance_eval &block
  113. end
  114.  
  115. def _list
  116. @list
  117. end
  118. end
  119.  
  120. class InvocationRecord
  121. attr_reader :dingus, :method, :args
  122. attr_accessor :value
  123.  
  124. def initialize(dingus, method, args = [], *optional)
  125. @dingus = dingus
  126. @method = method
  127. @args = args
  128. @value = optional.first
  129. @has_value = !optional.empty?
  130. end
  131.  
  132. def value=(v)
  133. @has_value = true
  134. @value = v
  135. end
  136.  
  137. def has_value?
  138. @has_value
  139. end
  140.  
  141. def eql?(b)
  142. self.method == b.method && ((self.args == b.args) || self.any? || b.any?)
  143. end
  144.  
  145. def ==(b)
  146. self.eql?(b)
  147. end
  148.  
  149. def any?
  150. @args && @args.first == :any
  151. end
  152.  
  153. def to_s
  154. "#{method}(#{args.map{|i|i.inspect}.join(", ")})"
  155. end
  156.  
  157. def inspect
  158. retval = @has_value ? " => #{@value.inspect}" : ""
  159. "#<Invocation \"#{self}\"#{retval}>"
  160. end
  161.  
  162. end
  163.  
  164. class InvocationResultHash
  165. def initialize
  166. @list = []
  167. end
  168.  
  169. def []=(invocation, value)
  170. @list << [invocation, value]
  171. value
  172. end
  173.  
  174. def [](invocation)
  175. @list.reverse.each do |inv, value|
  176. return value if inv == invocation
  177. end
  178. nil
  179. end
  180.  
  181. def defined?(invocation)
  182. @list.any? {|inv, value| inv == invocation}
  183. end
  184.  
  185. def empty?
  186. @list.empty?
  187. end
  188.  
  189. end
  190.  
  191. class InvocationList < Array
  192. def include?(method, *args)
  193. if method.is_a? Symbol
  194. self.any? do |i|
  195. i.method == method
  196. end
  197. else
  198. raise "Haven't implemented include? for anything but Symbols (given #{method.class})"
  199. end
  200. end
  201. alias_method :has_received?, :include?
  202.  
  203. def [](index)
  204. case index
  205. when Fixnum
  206. self.at(index)
  207. when Symbol
  208. InvocationList.new self.select{|i| i.method == index }
  209. else
  210. raise "Unknown index type: #{index.class}"
  211. end
  212. end
  213.  
  214. def count
  215. length
  216. end
  217.  
  218. end
  219.  
  220. end
  221.  
  222. def should_be_a_dingus(object)
  223. lambda {
  224. object._dingus?
  225. }.should_not raise_error(NoMethodError)
  226. end
  227.  
  228. def should_not_be_a_dingus(object)
  229. lambda {
  230. object._dingus?
  231. }.should raise_error(NoMethodError)
  232. end
  233.  
  234. describe RDingus::DefinitionProxy do
  235. before :each do
  236. @rdingus = RDingus.new
  237. @def = RDingus::DefinitionProxy.new(@rdingus)
  238. end
  239.  
  240. describe "when given a sub defining block" do
  241. before :each do
  242. @def._define do
  243. b_method :result
  244. end
  245. @invocation = RDingus::InvocationRecord.new(@rdingus, :b_method)
  246. end
  247.  
  248. it "should record the method's result" do
  249. should_not_be_a_dingus @def._list[@invocation]
  250. @def._list[@invocation].should == :result
  251. end
  252. end
  253.  
  254. describe "when calling methods to record" do
  255. before :each do
  256. @def.a_method(:arguments) { :return_value }
  257. @invocation = RDingus::InvocationRecord.new(@rdingus, :a_method)
  258. end
  259.  
  260. it "should record them in an InvocationResultHash" do
  261. @def._list.should be_kind_of(RDingus::InvocationResultHash)
  262. end
  263.  
  264. it "should use the given InvocationResultHash if present" do
  265. @hash = RDingus::InvocationResultHash.new
  266. @def = RDingus::DefinitionProxy.new @rdingus, @hash
  267. @def.a_method { :test }
  268. @def._list[@invocation].should_not be_nil
  269. end
  270.  
  271. describe "with a block" do
  272. it "should store the block" do
  273. @def.a_method { :block }
  274. should_not_be_a_dingus @def._list[@invocation]
  275. @def._list[@invocation].should be_instance_of(Proc)
  276. end
  277. end
  278.  
  279. describe "with just a return value" do
  280. it "should store the value" do
  281. @def.a_method :value
  282. should_not_be_a_dingus @def._list[@invocation]
  283. @def._list[@invocation].should == :value
  284. end
  285. end
  286.  
  287. describe "with no value or block" do
  288. it "should return a new dingus' definition proxy" do
  289. should_not_be_a_dingus @def.a_method._list
  290. @def.a_method._list.should be_empty
  291. end
  292. end
  293.  
  294. end
  295.  
  296. end
  297.  
  298. describe RDingus do
  299. before :each do
  300. @rdingus = RDingus.new
  301. end
  302.  
  303. it "should be recognized by the custom matcher 'be_a_dingus'" do
  304. should_be_a_dingus(@rdingus)
  305. end
  306.  
  307. it "should return an RDingus on method calls" do
  308. should_be_a_dingus(@rdingus.a_method)
  309. end
  310.  
  311. it "should return the same RDingus for the same method invocation" do
  312. @rdingus.a_method.__id__.should == @rdingus.a_method.__id__
  313. end
  314.  
  315. it "should return a different RDingus for a different method invocation" do
  316. @rdingus.a_method.__id__.should_not == @rdingus.b_method.__id__
  317. end
  318.  
  319. it "should return a different RDingus for the same method invocation but with different arguments" do
  320. @rdingus.a_method.__id__.should_not == @rdingus.a_method(:argument).__id__
  321. end
  322.  
  323. it "should return self on dup" do
  324. @rdingus.dup.__id__.should == @rdingus.__id__
  325. end
  326.  
  327. it "should return self on clone" do
  328. @rdingus.clone.__id__.should == @rdingus.__id__
  329. end
  330.  
  331. describe "when calling .inspect" do
  332. it "should not return an RDingus" do
  333. should_not_be_a_dingus @rdingus.inspect
  334. end
  335.  
  336. it "should return a string" do
  337. # This has to be done in reverse because "should" is not
  338. # defined on a dingus
  339. String.should === @rdingus.inspect
  340. end
  341. end
  342.  
  343. describe "when defining return values" do
  344. # Comparisons with a dingus can be tricky. They will always be "true"
  345. # (or technically "non nil/false") if compared with something else.
  346. # You have to check to make sure it's *not* a dingus before making
  347. # a comparison.
  348.  
  349. it "should return what's assigned" do
  350. @rdingus._define.a_method { :shell }
  351. should_not_be_a_dingus @rdingus.a_method
  352. :shell.should == @rdingus.a_method
  353. end
  354.  
  355. it "should return false if assigned" do
  356. @rdingus._define.a_method { false }
  357. should_not_be_a_dingus @rdingus.a_method
  358. false.should == @rdingus.a_method
  359. end
  360.  
  361. it "should return nil if assigned" do
  362. @rdingus._define.a_method { nil }
  363. should_not_be_a_dingus @rdingus.a_method
  364. nil.should == @rdingus.a_method
  365. end
  366.  
  367. it "should not remember as a method call" do
  368. @rdingus._define.a_method { "something" }
  369. @rdingus._calls.should be_empty
  370. end
  371.  
  372. it "should not forget other method calls" do
  373. @rdingus.remember_me
  374. @rdingus._define.a_method "something"
  375. @rdingus._calls.map{|i|i.method}.should == [:remember_me]
  376. end
  377.  
  378. describe "and within the result proc" do
  379.  
  380. it "should provide access to method name" do
  381. @rdingus._define.a_method { raise method.to_s }
  382. lambda { @rdingus.a_method }.should raise_error("a_method")
  383. end
  384.  
  385. it "should provide access to called arguments" do
  386. @rdingus._define.a_method("arguments") { raise args.first }
  387. lambda { @rdingus.a_method("arguments") }.should raise_error("arguments")
  388. end
  389.  
  390. it "should provide access to passed block" do
  391. @rdingus._define.a_method { raise block.call }
  392. lambda {
  393. @rdingus.a_method do
  394. "raise me"
  395. end
  396. }.should raise_error("raise me")
  397. end
  398.  
  399. end
  400.  
  401. describe "of nested method calls" do
  402. it "should return what's assigned" do
  403. @rdingus._define.a_method.b_method :stump
  404. should_not_be_a_dingus @rdingus.a_method.b_method
  405. :stump.should == @rdingus.a_method.b_method
  406. end
  407.  
  408. it "should not remember as a method call" do
  409. @rdingus._define.a_method.b_method "something"
  410. @rdingus._calls.should be_empty
  411. end
  412. end
  413.  
  414. describe "with arbitrary arguments" do
  415. before :each do
  416. @rdingus._define.a_method(:any) { :stump }
  417. end
  418.  
  419. it "should return what's assigned given any arguments" do
  420. Symbol.should === @rdingus.a_method(:argument)
  421. :stump.should == @rdingus.a_method(:argument)
  422. end
  423.  
  424. it "should return what's assigned with no arguments" do
  425. Symbol.should === @rdingus.a_method
  426. :stump.should == @rdingus.a_method
  427. end
  428. end
  429.  
  430. describe "with specific arguments" do
  431. before :each do
  432. @rdingus._define.a_method(:argument) { :stump }
  433. end
  434.  
  435. it "should return what's assigned" do
  436. Symbol.should === @rdingus.a_method(:argument)
  437. :stump.should == @rdingus.a_method(:argument)
  438. end
  439.  
  440. it "should return an RDingus if arguments don't match" do
  441. should_be_a_dingus @rdingus.a_method(:argument, :argument2)
  442. end
  443. end
  444.  
  445. describe "with block syntax" do
  446. before :each do
  447. @rdingus._define do
  448. a_method { :stump }
  449. end
  450. end
  451.  
  452. it "should return what's assigned" do
  453. should_not_be_a_dingus @rdingus.a_method
  454. @rdingus.a_method.should == :stump
  455. end
  456.  
  457. describe "within a block syntax" do
  458. it "should return what's assigned" do
  459. @rdingus._define do
  460. a_method._define do
  461. b_method :stump
  462. end
  463. end
  464. should_not_be_a_dingus @rdingus.a_method.b_method
  465. @rdingus.a_method.b_method.should == :stump
  466. end
  467. end
  468. end
  469. end
  470.  
  471. describe "when specifying exceptions" do
  472. it "should raise given exception" do
  473. @rdingus._define.raise_me { raise "Exception" }
  474. lambda { @rdingus.raise_me }.should raise_error("Exception")
  475. end
  476.  
  477. it "should not remember as a method call" do
  478. @rdingus._define.raise_me {raise "Exception" }
  479. lambda { @rdingus.raise_me }.should raise_error("Exception")
  480. @rdingus._calls.map{|i|i.method}.should == [:raise_me]
  481. end
  482.  
  483. it "should not forget other method calls" do
  484. @rdingus.remember_me
  485. @rdingus._define.raise_me { raise "Exception" }
  486. lambda { @rdingus.raise_me }.should raise_error("Exception")
  487. @rdingus._calls.map{|i|i.method}.should == [:remember_me, :raise_me]
  488. end
  489. end
  490.  
  491. describe "recording invocations" do
  492. it "should remember methods invoked" do
  493. @rdingus.sandwich
  494. :sandwich.should == @rdingus._calls.first.method
  495. end
  496.  
  497. it "should remember with arguments invoked" do
  498. @rdingus.sandwich(:with_cheese)
  499. :with_cheese.should == @rdingus._calls.first.args.first
  500. end
  501.  
  502. it "should count method invocations" do
  503. @rdingus.sandwich
  504. @rdingus._calls.count.should == 1
  505. end
  506.  
  507. end
  508.  
  509. end
  510.  
  511.  
  512. describe RDingus::InvocationRecord do
  513. before :each do
  514. @rdingus = RDingus.new
  515. @invocation_record = RDingus::InvocationRecord.new(@rdingus, :method, [:arg1, :arg2])
  516. end
  517.  
  518. it "should provide access to it's owning RDingus" do
  519. @invocation_record.dingus.should == @rdingus
  520. end
  521.  
  522. it "should be equal for same method and arguments" do
  523. RDingus::InvocationRecord.new(@rdingus, :method, [:arg1, :arg2]).should == @invocation_record
  524. end
  525.  
  526. it "should not be equal for different method" do
  527. RDingus::InvocationRecord.new(@rdingus, :method2, [:arg1, :arg2]).should_not == @invocation_record
  528. end
  529.  
  530. it "should not be equal for different arguments" do
  531. RDingus::InvocationRecord.new(@rdingus, :method, [:arg2, :arg3]).should_not == @invocation_record
  532. end
  533.  
  534. it "should define == the same as eql?" do
  535. RDingus::InvocationRecord.new(@rdingus, :method, [:arg1, :arg2]).should be_eql(@invocation_record)
  536. end
  537.  
  538. describe "when told to accept any arguments" do
  539. before :each do
  540. @invocation_record = RDingus::InvocationRecord.new(@rdingus, :method, [:any])
  541. end
  542.  
  543. it "should be equal for same method and same arguments" do
  544. RDingus::InvocationRecord.new(@rdingus, :method, [:any]).should == @invocation_record
  545. end
  546.  
  547. it "should be equal for same method and different arguments" do
  548. RDingus::InvocationRecord.new(@rdingus, :method, [:other, :args]).should == @invocation_record
  549. end
  550.  
  551. it "should not be equal for different method" do
  552. RDingus::InvocationRecord.new(@rdingus, :method2, [:arg1, :arg2]).should_not == @invocation_record
  553. end
  554. end
  555.  
  556. it "should convert to a useful string" do
  557. @invocation_record.to_s.should == "method(:arg1, :arg2)"
  558. end
  559.  
  560. it "should have a useful inspect result" do
  561. @invocation_record.value = :arg3
  562. @invocation_record.inspect.should == "#<Invocation \"method(:arg1, :arg2)\" => :arg3>"
  563. end
  564. end
  565.  
  566. describe RDingus::InvocationResultHash do
  567. before :each do
  568. @rdingus = RDingus.new
  569. @invocation = RDingus::InvocationRecord.new(@rdingus, :method, [:arg1, :arg2])
  570. @invocation_list = RDingus::InvocationResultHash.new
  571. end
  572.  
  573. it "should store and retrieve for same invocation" do
  574. @invocation_list[@invocation] = "test"
  575. @invocation_list[@invocation].should == "test"
  576. end
  577.  
  578. it "should store and retrieve for equivalent invocations" do
  579. @invocation_list[RDingus::InvocationRecord.new(@rdingus, :method, [:any])] = 'test'
  580. @invocation_list[@invocation].should == "test"
  581. end
  582.  
  583. it "should not store and retrieve for non equivalent invocations" do
  584. @invocation_list[RDingus::InvocationRecord.new(@rdingus, :method2)] = "test"
  585. @invocation_list[@invocation].should_not == "test"
  586. end
  587.  
  588. it "should know if a value is defined" do
  589. @invocation_list[@invocation] = "test"
  590. @invocation_list.defined?(@invocation).should be_true
  591. end
  592.  
  593. it "should know it has a value even if value is nil" do
  594. @invocation_list[@invocation] = false
  595. @invocation_list[@invocation].should == false
  596. @invocation_list.defined?(@invocation).should be_true
  597. end
  598.  
  599. it "should know it has a value even if value is false" do
  600. @invocation_list[@invocation] = nil
  601. @invocation_list[@invocation].should == nil
  602. @invocation_list.defined?(@invocation).should be_true
  603. end
  604.  
  605. describe "when redefining invocation" do
  606. it "should use the last one" do
  607. @invocation_list[@invocation] = "first"
  608. @invocation_list[RDingus::InvocationRecord.new(@rdingus, :method, [:any])] = "second"
  609. @invocation_list[@invocation].should == "second"
  610. end
  611. end
  612.  
  613. end
  614.  
  615. describe RDingus::InvocationList do
  616. before :each do
  617. @rdingus = RDingus.new
  618. @list = RDingus::InvocationList.new
  619. end
  620.  
  621. describe "when adding method invocations" do
  622. it "should allow append" do
  623. @list << :test
  624. @list.to_a.should == [:test]
  625. end
  626. end
  627.  
  628. describe "when counting method invocations" do
  629. before :each do
  630. @list << RDingus::InvocationRecord.new(@rdingus, :some_method)
  631. @list << RDingus::InvocationRecord.new(@rdingus, :some_method_2)
  632. end
  633.  
  634. describe "without sub calls" do
  635. it "should be equal to length" do
  636. @list.count.should == @list.length
  637. end
  638. end
  639.  
  640. end
  641.  
  642. describe "when querying using include?" do
  643.  
  644. describe "with just a method name symbol" do
  645. before :each do
  646. @invocation = RDingus::InvocationRecord.new(@rdingus, :some_method)
  647. end
  648.  
  649. describe "that is in the list" do
  650. before :each do
  651. @list << @invocation
  652. end
  653.  
  654. it "should find the invocation" do
  655. @list.should include(:some_method)
  656. end
  657. end
  658.  
  659. describe "that is not in the list" do
  660. it "should not find the invocation" do
  661. @list.should_not include(:some_method)
  662. end
  663. end
  664. end
  665.  
  666. it "should alias has_received? to include?" do
  667. @list << RDingus::InvocationRecord.new(@rdingus, :some_method)
  668. @list.should have_received(:some_method)
  669. end
  670.  
  671. end
  672.  
  673. describe "when indexing via []" do
  674. before :each do
  675. @invocation1 = RDingus::InvocationRecord.new(@rdingus, :some_method)
  676. @invocation2 = RDingus::InvocationRecord.new(@rdingus, :method_2, [:arg1, :arg2])
  677. @invocation3 = RDingus::InvocationRecord.new(@rdingus, :method_3)
  678. @list << @invocation1; @list << @invocation2; @list << @invocation3
  679. end
  680.  
  681. describe "with integers" do
  682. it "should return the requested invocation in the sequence" do
  683. @list[2].method.should == :method_3
  684. end
  685. end
  686.  
  687. describe "with a method name symbol" do
  688. before :each do
  689. @list << @invocation2
  690. end
  691.  
  692. it "should return a new list" do
  693. @list[:method_2].should be_kind_of(RDingus::InvocationList)
  694. end
  695.  
  696. it "should have proper number of elements" do
  697. @list[:method_2].length.should == 2
  698. end
  699.  
  700. it "should only have invocations of the given method" do
  701. @list[:method_2].each {|i| i.method.should == :method_2 }
  702. end
  703. end
  704.  
  705. end
  706.  
  707. end
  708.  
  709.  
  710.  
  711. class Person
  712. attr_accessor :name
  713. attr_accessor :shoe_size
  714.  
  715. def eat(sandwich)
  716. @sandwich = sandwich
  717. @sandwich.cut :in => 2
  718. @sandwich.take_a_bite
  719. end
  720.  
  721. def drop(sandwich)
  722. sandwich.drop
  723. end
  724.  
  725. def opinion_of_sandwich
  726. if @sandwich.cheese.spicy?
  727. "yummy!"
  728. else
  729. "bland"
  730. end
  731. end
  732.  
  733. end
  734.  
  735. describe Person do
  736. before :each do
  737. @person = Person.new
  738. end
  739.  
  740. describe "when specifying a shoe size" do
  741.  
  742. it "should save the shoe size" do
  743. @person.shoe_size = 4
  744. @person.shoe_size.should == 4
  745. end
  746.  
  747. end
  748.  
  749. describe "when using a sandwich" do
  750. before :each do
  751. @sandwich = RDingus.new
  752. @person.eat(@sandwich)
  753. end
  754.  
  755. it "should be able to cut a sandwhich" do
  756. @sandwich._calls.first.method.should == :cut
  757. end
  758.  
  759. it "should cut into two slices" do
  760. @sandwich._calls[0].args[0].should == {:in => 2}
  761. end
  762.  
  763. it "should take a bite" do
  764. @sandwich._calls.should have_received(:take_a_bite)
  765. end
  766.  
  767. it "should not have been dropped" do
  768. @sandwich._calls.should_not have_received(:dropped)
  769. end
  770.  
  771. it "should raise an exception if it drops the sandwich" do
  772. @sandwich._define.drop { raise "Dropped Sandwich!" }
  773. lambda { @person.drop(@sandwich) }.should raise_error("Dropped Sandwich!")
  774. end
  775.  
  776. describe "that has spicy cheese" do
  777. before :each do
  778. @sandwich._define.cheese.spicy? true
  779. end
  780.  
  781. it "should have an opinion of 'yummy!'" do
  782. @person.opinion_of_sandwich.should == "yummy!"
  783. end
  784. end
  785.  
  786. describe "that does not have spicy cheese (block syntax)" do
  787. before :each do
  788. @sandwich._define do
  789. cheese.spicy? false
  790. end
  791. end
  792.  
  793. it "should have an opinion of 'bland'" do
  794. @person.opinion_of_sandwich.should == "bland"
  795. end
  796. end
  797.  
  798. describe "that does not have spicy cheese" do
  799. before :each do
  800. @sandwich._define.cheese.spicy? false
  801. end
  802.  
  803. it "should have an opinion of 'bland'" do
  804. @person.opinion_of_sandwich.should == "bland"
  805. end
  806. end
  807. end
  808.  
  809. end
Add Comment
Please, Sign In to add comment