├── .gitignore ├── .rspec ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── README.md ├── ch09-writing-your-own-methods ├── ask.rb ├── old_school_roman_numerals.rb └── roman_numerals.rb ├── ch10-nothing-new ├── dictionary_sort.rb ├── english_number.rb ├── ninety_nine_bottles_of_beer.rb ├── shuffle.rb └── sort.rb ├── ch11-reading-and-writing ├── build_a_better_playlist.rb ├── build_your_own_playlist.rb └── safer_picture_downloading.rb ├── ch12-new-classes-of-objects ├── birthday_helper.rb ├── happy_birthday.rb ├── one_billion_seconds.rb └── party_like_its_roman_to_integer_mcmxcix.rb ├── ch13-creating-new-classes ├── extend_built_in_classes.rb ├── interactive_baby_dragon.rb └── orange_tree.rb ├── ch14-blocks-and-procs ├── better_program_logger.rb ├── even_better_profiling.rb ├── grandfather_clock.rb └── program_logger.rb ├── solutions ├── ch09-writing-your-own-methods │ ├── ask │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb │ ├── old-school-roman-numerals │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb │ └── roman_numerals │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb ├── ch10-nothing-new │ ├── dictionary_sort │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb │ ├── english_number │ │ └── how_you_and_i_could_do_it.rb │ ├── ninety_nine_bottles_of_beer │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb │ ├── shuffle │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb │ └── sort │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb ├── ch11-reading-and-writing │ ├── build_a_better_playlist │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb │ ├── build_your_own_playlist │ │ └── how_you_and_i_could_do_it.rb │ └── safer_picture_downloading │ │ └── how_i_did_it_on_windows.rb ├── ch12-new-classes-of-objects │ ├── birthday_helper │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb │ ├── happy_birthday │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb │ ├── one_billion_seconds │ │ └── how_i_would_do_it.rb │ └── party_like_its_roman_to_integer_mcmxcix.rb │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb ├── ch13-creating-new-classes │ ├── extend_the_built_in_classes │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb │ ├── interactive_baby_dragon │ │ ├── how_i_would_do_it.rb │ │ └── how_you_could_do_it.rb │ └── orange_tree │ │ └── how_you_and_i_could_do_it.rb └── ch14-blocks-and-procs │ ├── better_program_logger │ ├── how_i_would_do_it.rb │ └── how_you_could_do_it.rb │ ├── even_better_profiling │ ├── how_i_would_do_it.rb │ └── how_you_could_do_it.rb │ ├── grandfather_clock │ ├── how_i_would_do_it.rb │ └── how_you_could_do_it.rb │ └── program_logger │ ├── how_i_would_do_it.rb │ └── how_you_could_do_it.rb └── spec ├── ch09 ├── ask_spec.rb ├── old_school_roman_numerals_spec.rb └── roman_numeral_spec.rb ├── ch10 ├── dictionary_sort_spec.rb ├── english_number_spec.rb ├── ninety_nine_bottles_of_beer_spec.rb ├── shuffle_spec.rb └── sort_spec.rb ├── ch11 └── build_a_better_playlist_spec.rb ├── ch12 └── roman_to_integer_spec.rb ├── ch13 ├── extend_the_built_in_classes_spec.rb └── orange_tree_spec.rb ├── ch14 ├── better_program_logger_spec.rb ├── even_better_profiling_spec.rb ├── grandfather_clock_spec.rb └── program_logger_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | /**/.DS_Store 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | --require spec_helper 4 | --require set 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | rvm: 3 | - 2.2.2 4 | script: 5 | - bundle exec rspec spec 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'rspec' 4 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | diff-lcs (1.2.5) 5 | rspec (3.2.0) 6 | rspec-core (~> 3.2.0) 7 | rspec-expectations (~> 3.2.0) 8 | rspec-mocks (~> 3.2.0) 9 | rspec-core (3.2.3) 10 | rspec-support (~> 3.2.0) 11 | rspec-expectations (3.2.1) 12 | diff-lcs (>= 1.2.0, < 2.0) 13 | rspec-support (~> 3.2.0) 14 | rspec-mocks (3.2.1) 15 | diff-lcs (>= 1.2.0, < 2.0) 16 | rspec-support (~> 3.2.0) 17 | rspec-support (3.2.2) 18 | 19 | PLATFORMS 20 | ruby 21 | 22 | DEPENDENCIES 23 | rspec 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learn To Program 2 | 3 | ##Ruby Week 4 | 5 | Now that you have completed the first week of the PreCourse and need to continue developing your Ruby skills, we recommend that you continue with [Chris Pine's Learn To Program book 2nd Edition](https://drive.google.com/file/d/0Bz17qR4zZedib0M5RnRwWFl3MUk/view). **Please complete chapters 9-14.** 6 | 7 | Please attempt every exercise, submitting the code to us using your newfound git and GitHub skils by forking [https://github.com/makersacademy/learn_to_program](https://github.com/makersacademy/learn_to_program), and submitting a pull request (Note that submitting a pull request will kick off some [Continuous Integration](https://github.com/makersacademy/pre_course/blob/master/pills/continuous_integration.md) <-- see link for more details). 8 | 9 | Do not spend more than an hour on any individual exercise. The solutions are included at the end of the book. This week it is better that you cover all the chapters, and review the solutions for any exercises that you cannot complete in under an hour. Please do not look at any individual solution until you have at least tried each individual exercise. 10 | 11 | ###Setup Instructions 12 | 13 | - Fork the repo to your own 14 | - Push to your own fork (check the upstream) 15 | - Open a pull request straight away 16 | - Commit and push your code after *each* challenge so the CI server can check your work as you go along. 17 | - You must get a green tick at the end of the exercise from the CI to pass the week's challenge. 18 | 19 | ###For submitting exercises from the Chris Pine Learn to Program Book 20 | 21 | If you would like to run the tests associated with each chapter or even individual exercies you can do so as follows. We use the RSpec library to test the code is working properly. The RSpec library is available as a Ruby 'gem', which you can install in your computer by typing the following at the command line prompt: 22 | 23 | ```sh 24 | $ gem install rspec 25 | ``` 26 | 27 | Once you have RSpec installed as above, ensure that you have navigated to the root directory of this learn_to_program repo, e.g. 28 | 29 | ```sh 30 | cd /Users/JohnDoe/Projects/MakersAcademy/learn_to_program 31 | ``` 32 | 33 | Then if you'd like to check, say, your solution for the chapter 9 'ask' program you can run the following command: 34 | 35 | ```sh 36 | rspec spec/ch09/ask_spec.rb 37 | ``` 38 | 39 | You might find the output of the command a little difficult to interpret, but in general the output will have a green colour when the test is passing, and a red colour when some part of the test is failing. At this stage it is *not* necessary for you to understand how RSpec works, or even be able to make all the tests pass. We just provide these notes in case you are interested in running the tests locally. 40 | 41 | If you are curious feel free to inspect the contents of files like spec/ch09/ask_spec.rb - the contents are pure Ruby, but they use parts of the RSpec library that will make them look very unfamiliar. You'll be covering RSpec in some detail in the week 1 of the main Makers Academy course, so if this all seems to complicated, just leave it for now, and focus on your best effort to write the code for the Learn to Program exercises. 42 | 43 | If you are keen to explore more of the tests and would like to run more than one at a time please use the following commands: 44 | 45 | ```sh 46 | rspec spec/ch09/ 47 | ``` 48 | 49 | will run all the tests for the chapter 9 exercises (that have tests) and 50 | 51 | ```sh 52 | rspec spec/ch10/ 53 | ``` 54 | 55 | will run all the ch10 tests and so on. If you want to run every single test at once you can run: 56 | 57 | ```sh 58 | rspec 59 | ``` 60 | 61 | but be prepared for potentially quite a lot of output! :-) 62 | -------------------------------------------------------------------------------- /ch09-writing-your-own-methods/ask.rb: -------------------------------------------------------------------------------- 1 | def ask question 2 | # your code here 3 | end -------------------------------------------------------------------------------- /ch09-writing-your-own-methods/old_school_roman_numerals.rb: -------------------------------------------------------------------------------- 1 | def old_roman_numeral num 2 | # your code here 3 | end -------------------------------------------------------------------------------- /ch09-writing-your-own-methods/roman_numerals.rb: -------------------------------------------------------------------------------- 1 | def roman_numeral num 2 | # your code here 3 | end -------------------------------------------------------------------------------- /ch10-nothing-new/dictionary_sort.rb: -------------------------------------------------------------------------------- 1 | def dictionary_sort arr 2 | # your code here 3 | end -------------------------------------------------------------------------------- /ch10-nothing-new/english_number.rb: -------------------------------------------------------------------------------- 1 | def english_number number 2 | # your code here 3 | end 4 | -------------------------------------------------------------------------------- /ch10-nothing-new/ninety_nine_bottles_of_beer.rb: -------------------------------------------------------------------------------- 1 | # your code here -------------------------------------------------------------------------------- /ch10-nothing-new/shuffle.rb: -------------------------------------------------------------------------------- 1 | def shuffle arr 2 | # your code here 3 | end -------------------------------------------------------------------------------- /ch10-nothing-new/sort.rb: -------------------------------------------------------------------------------- 1 | def sort arr 2 | # your code here 3 | end -------------------------------------------------------------------------------- /ch11-reading-and-writing/build_a_better_playlist.rb: -------------------------------------------------------------------------------- 1 | def music_shuffle filenames 2 | # your code here 3 | end 4 | -------------------------------------------------------------------------------- /ch11-reading-and-writing/build_your_own_playlist.rb: -------------------------------------------------------------------------------- 1 | # your code here -------------------------------------------------------------------------------- /ch11-reading-and-writing/safer_picture_downloading.rb: -------------------------------------------------------------------------------- 1 | # your code here -------------------------------------------------------------------------------- /ch12-new-classes-of-objects/birthday_helper.rb: -------------------------------------------------------------------------------- 1 | # your code here -------------------------------------------------------------------------------- /ch12-new-classes-of-objects/happy_birthday.rb: -------------------------------------------------------------------------------- 1 | # your code here -------------------------------------------------------------------------------- /ch12-new-classes-of-objects/one_billion_seconds.rb: -------------------------------------------------------------------------------- 1 | # your code here -------------------------------------------------------------------------------- /ch12-new-classes-of-objects/party_like_its_roman_to_integer_mcmxcix.rb: -------------------------------------------------------------------------------- 1 | def roman_to_integer roman 2 | # your code here 3 | end -------------------------------------------------------------------------------- /ch13-creating-new-classes/extend_built_in_classes.rb: -------------------------------------------------------------------------------- 1 | class Integer 2 | # your code here 3 | end -------------------------------------------------------------------------------- /ch13-creating-new-classes/interactive_baby_dragon.rb: -------------------------------------------------------------------------------- 1 | # your code here -------------------------------------------------------------------------------- /ch13-creating-new-classes/orange_tree.rb: -------------------------------------------------------------------------------- 1 | # in order to pass the rspec please follow the below rates of growth, orange production and age of death. 2 | # have your OrangeTree grow by 0.4 per year. 3 | # have it produce no oranges in its first 5 years 4 | # starting in its sixth year have it produce oranges at a rate of (height * 15 - 25) per year. 5 | # have the tree die after 25 years. 6 | # check out the rspec spec/ch13/orange_tree_spec.rb to see what strings we're looking for in the responses. 7 | 8 | 9 | class OrangeTree 10 | # your code here 11 | end 12 | -------------------------------------------------------------------------------- /ch14-blocks-and-procs/better_program_logger.rb: -------------------------------------------------------------------------------- 1 | def log desc, &block 2 | # your code here 3 | end -------------------------------------------------------------------------------- /ch14-blocks-and-procs/even_better_profiling.rb: -------------------------------------------------------------------------------- 1 | def profile block_description, &block 2 | # your code here 3 | end -------------------------------------------------------------------------------- /ch14-blocks-and-procs/grandfather_clock.rb: -------------------------------------------------------------------------------- 1 | def grandfather_clock &block 2 | # your code here 3 | end -------------------------------------------------------------------------------- /ch14-blocks-and-procs/program_logger.rb: -------------------------------------------------------------------------------- 1 | def log desc, &block 2 | # your code here 3 | end -------------------------------------------------------------------------------- /solutions/ch09-writing-your-own-methods/ask/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | def ask question 2 | while true 3 | puts question 4 | reply = gets.chomp.downcase 5 | 6 | return true if reply == 'yes' 7 | return false if reply == 'no' 8 | 9 | puts 'Please answer "yes" or "no".' 10 | end 11 | answer # This is what we return (true or false). 12 | end 13 | 14 | # puts(ask('Do you like eating tacos?')) -------------------------------------------------------------------------------- /solutions/ch09-writing-your-own-methods/ask/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def ask question 2 | while true 3 | puts question 4 | reply = gets.chomp.downcase 5 | 6 | if reply == 'yes' 7 | return true 8 | end 9 | 10 | if reply == 'no' 11 | return false 12 | end 13 | 14 | # If we got this far, then we're going to loop 15 | # and ask the question again. 16 | puts 'Please answer "yes" or "no".' 17 | end 18 | answer # This is what we return (true or false). 19 | end 20 | 21 | # likes_it = ask 'Do you like eating tacos?' 22 | # puts likes_it -------------------------------------------------------------------------------- /solutions/ch09-writing-your-own-methods/old-school-roman-numerals/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | def old_roman_numeral num 2 | raise 'Must use positive integer' if num <= 0 3 | 4 | roman = '' 5 | 6 | roman << 'M' * (num / 1000) 7 | roman << 'D' * (num % 1000 / 500) 8 | roman << 'C' * (num % 500 / 100) 9 | roman << 'L' * (num % 100 / 50) 10 | roman << 'X' * (num % 50 / 10) 11 | roman << 'V' * (num % 10 / 5) 12 | roman << 'I' * (num % 5 / 1) 13 | 14 | roman 15 | end 16 | 17 | # puts(old_roman_numeral(1999)) -------------------------------------------------------------------------------- /solutions/ch09-writing-your-own-methods/old-school-roman-numerals/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def old_roman_numeral num 2 | roman = '' 3 | 4 | roman = roman + 'M' * (num / 1000) 5 | roman = roman + 'D' * (num % 1000 / 500) 6 | roman = roman + 'C' * (num % 500 / 100) 7 | roman = roman + 'L' * (num % 100 / 50) 8 | roman = roman + 'X' * (num % 50 / 10) 9 | roman = roman + 'V' * (num % 10 / 5) 10 | roman = roman + 'I' * (num % 5 / 1) 11 | roman 12 | end 13 | 14 | # puts(old_roman_numeral(1999)) -------------------------------------------------------------------------------- /solutions/ch09-writing-your-own-methods/roman_numerals/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | def roman_numeral num 2 | raise 'Must use positive integer' if num <= 0 3 | 4 | digit_vals = [['I', 5, 1], 5 | ['V', 10, 5], 6 | ['X', 50, 10], 7 | ['L', 100, 50], 8 | ['C', 500, 100], 9 | ['D', 1000, 500], 10 | ['M', nil, 1000]] 11 | 12 | roman = '' 13 | remaining = nil 14 | 15 | # Build string "roman" in reverse. 16 | build_rev = proc do |l,m,n| 17 | num_l = m ? (num % m / n) : (num / n) 18 | full = m && (num_l == (m/n - 1)) 19 | 20 | if full && (num_l>1 || remaining) 21 | # must carry 22 | remaining ||= l # carry l if not already carrying 23 | else 24 | if remaining 25 | roman << l + remaining 26 | remaining = nil end 27 | roman << l * num_l 28 | end 29 | end 30 | digit_vals.each {|l,m,n| build_rev[l,m,n]} 31 | 32 | roman.reverse 33 | end 34 | 35 | # puts(old_roman_numeral(1999)) -------------------------------------------------------------------------------- /solutions/ch09-writing-your-own-methods/roman_numerals/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def roman_numeral num 2 | thous = (num / 1000) 3 | hunds = (num % 1000 / 100) 4 | tens = (num % 100 / 10) 5 | ones = (num % 10) 6 | 7 | roman = 'M' * thous 8 | 9 | if hunds == 9 10 | roman = roman + 'CM' 11 | elsif hunds == 4 12 | roman = roman + 'CD' 13 | else 14 | roman = roman + 'D' * (num % 1000 / 500) 15 | roman = roman + 'C' * (num % 500 / 100) 16 | end 17 | 18 | if tens == 9 19 | roman = roman + 'XC' 20 | elsif tens == 4 21 | roman = romann + 'XL' 22 | else 23 | roman = roman + 'L' * (num % 100 / 50) 24 | roman = roman + 'X' * (num % 50 / 10) 25 | end 26 | 27 | if ones == 9 28 | roman = roman + 'IX' 29 | elsif ones == 4 30 | roman = roman + 'IV' 31 | else 32 | roman = roman + 'V' * (num % 10 / 5) 33 | roman = roman + 'I' * (num % 5 / 1) 34 | end 35 | roman 36 | end 37 | 38 | # puts(roman_numeral(1999)) -------------------------------------------------------------------------------- /solutions/ch10-nothing-new/dictionary_sort/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | # The well-known quicksort algorithm. 2 | def dictionary_sort arr 3 | return arr if arr.length <= 1 4 | 5 | middle = arr.pop 6 | less = arr.select{|x| x.downcase < middle.downcase} 7 | more = arr.select{|x| x.downcase >= middle.downcase} 8 | 9 | dictionary_sort(less) + [middle] + dictionary_sort(more) 10 | end 11 | 12 | # words = ['can','feel','singing.','like','A','can'] 13 | # puts(dictionary_sort(words).join(' ')) -------------------------------------------------------------------------------- /solutions/ch10-nothing-new/dictionary_sort/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def dictionary_sort arr 2 | rec_dict_sort arr, [] 3 | end 4 | 5 | def rec_dict_sort unsorted, sorted 6 | if unsorted.length <= 0 7 | return sorted 8 | end 9 | 10 | # So if we got here, then it means we still 11 | # have work to do. 12 | 13 | smallest = unsorted.pop 14 | still_unsorted = [] 15 | 16 | unsorted.each do |tested_object| 17 | if tested_object.downcase < smallest.downcase 18 | still_unsorted.push smallest 19 | smallest = tested_object 20 | else 21 | still_unsorted.push tested_object 22 | end 23 | end 24 | # Now "smallest" really does point to the 25 | # smallest element that "unsorted" contained, 26 | # and all the rest of it is in "still_unsorted". 27 | sorted.push smallest 28 | 29 | rec_dict_sort still_unsorted, sorted 30 | end 31 | 32 | # puts(dictionary_sort([' can' ,' feel' ,' singing.' ,' like' ,' A' ,' can' ])) -------------------------------------------------------------------------------- /solutions/ch10-nothing-new/english_number/how_you_and_i_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def english_number number 2 | if number < 0 # No negative numbers. 3 | return 'Please enter a number that isn\'t negative.' 4 | end 5 | if number == 0 6 | return 'zero' 7 | end 8 | 9 | # No more special cases! No more returns! 10 | 11 | num_string = '' # This is the string we will return. 12 | 13 | ones_place = ['one', 'two', 'three', 14 | 'four', 'five', 'six', 15 | 'seven', 'eight', 'nine'] 16 | 17 | tens_place = ['ten', 'twenty', 'thirty', 18 | 'forty', 'fifty', 'sixty', 19 | 'seventy', 'eighty', 'ninety'] 20 | 21 | 22 | teenagers = ['eleven', 'twelve', 'thirteen', 23 | 'fourteen', 'fifteen', 'sixteen', 24 | 'seventeen', 'eighteen', 'nineteen'] 25 | 26 | zillions = [['hundred', 2], 27 | ['thousand', 3], 28 | ['million', 6], 29 | ['billion', 9], 30 | ['trillion', 12], 31 | ['quadrillion', 15], 32 | ['quintillion', 18], 33 | ['sextillion', 21], 34 | ['septillion', 24], 35 | ['octillion', 27], 36 | ['nonillion', 30], 37 | ['decillion', 33], 38 | ['undecillion', 36], 39 | ['duodecillion', 39], 40 | ['tredecillion', 42], 41 | ['quattuordecillion', 45], 42 | ['quindecillion', 48], 43 | ['sexdecillion', 51], 44 | ['septendecillion', 54], 45 | ['octodecillion', 57], 46 | ['novemdecillion', 60], 47 | ['vigintillion', 63], 48 | ['googol', 100]] 49 | 50 | # "left" is how much of the number 51 | # we still have left to write out. 52 | # "write" is the part we are 53 | # writing out right now. 54 | # write and left...get it? :) 55 | left = number 56 | 57 | while zillions.length > 0 58 | zil_pair = zillions.pop 59 | zil_name = zil_pair[0] 60 | zil_base = 10 ** zil_pair[1] 61 | write = left/zil_base # How many zillions left? 62 | left = left - write*zil_base # Subtract off those zillions. 63 | 64 | if write > 0 65 | # Now here's the recursion: 66 | prefix = english_number write 67 | num_string = num_string + prefix + ' ' + zil_name 68 | 69 | if left > 0 70 | # So we don't write 'two billionfifty-one'... 71 | num_string = num_string + ' ' 72 | end 73 | end 74 | end 75 | 76 | write = left/10 # How many tens left? 77 | left = left - write*10 # Subtract off those tens. 78 | 79 | if write > 0 80 | if ((write == 1) and (left > 0)) 81 | # Since we can't write "tenty-two" instead of 82 | # "twelve", we have to make a special exception 83 | # for these. 84 | num_string = num_string + teenagers[left-1] 85 | # The "-1" is because teenagers[3] is 86 | # 'fourteen', not 'thirteen'. 87 | 88 | # Since we took care of the digit in the 89 | # ones place already, we have nothing left to write. 90 | left = 0 91 | else 92 | num_string = num_string + tens_place[write-1] 93 | # The "-1" is because tens_place[3] is 94 | # 'forty', not 'thirty'. 95 | end 96 | 97 | if left > 0 98 | # So we don't write 'sixtyfour'... 99 | num_string = num_string + '-' 100 | end 101 | end 102 | 103 | write = left # How many ones left to write out? 104 | left = 0 # Subtract off those ones. 105 | 106 | if write > 0 107 | num_string = num_string + ones_place[write-1] 108 | # The "-1" is because ones_place[3] is 109 | # 'four', not 'three'. 110 | end 111 | 112 | # Now we just return "num_string"... 113 | num_string 114 | end 115 | 116 | # puts english_number( 0) 117 | # puts english_number( 9) 118 | # puts english_number( 10) 119 | # puts english_number( 11) 120 | # puts english_number( 17) 121 | # puts english_number( 32) 122 | # puts english_number( 88) 123 | # puts english_number( 99) 124 | # puts english_number(100) 125 | # puts english_number(101) 126 | # puts english_number(234) 127 | # puts english_number(3211) 128 | # puts english_number(999999) 129 | # puts english_number(1000000000000) 130 | # puts english_number(109238745102938560129834709285360238475982374561034) 131 | -------------------------------------------------------------------------------- /solutions/ch10-nothing-new/ninety_nine_bottles_of_beer/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | # english_number as above, plus this: 2 | 3 | num_at_start = 5 # change to 9999 if you want 4 | 5 | num_bot = proc { |n| "#{english_number n} bottle#{n == 1 ? '' : 's'}" } 6 | 7 | num_at_start.downto(2) do |num| 8 | puts "#{num_bot[num]} of beer on the wall, #{num_bot[num]} of beer!".capitalize 9 | puts "Take one down, pass it around, #{num_bot[num-1]} of beer on the wall!" 10 | end 11 | 12 | puts "#{num_bot[1]} of beer on the wall, #{num_bot[1]} of beer!".capitalize 13 | puts "Take one down, pass it around, no more bottles of beer on the wall!" 14 | -------------------------------------------------------------------------------- /solutions/ch10-nothing-new/ninety_nine_bottles_of_beer/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | # english_number as above, plus this: 2 | num_at_start = 5 # change to 9999 if you want 3 | num_now = num_at_start 4 | while num_now > 2 5 | puts english_number(num_now).capitalize + ' bottles of beer on the wall, ' + 6 | english_number(num_now) + ' bottles of beer!' 7 | num_now = num_now - 1 8 | puts 'Take one down, pass it around, ' + 9 | english_number(num_now) + ' bottles of beer on the wall!' 10 | end 11 | 12 | # puts "Two bottles of beer on the wall, two bottles of beer!" 13 | # puts "Take one down, pass it around, one bottle of beer on the wall!" 14 | # puts "One bottle of beer on the wall, one bottle of beer!" 15 | # puts "Take one down, pass it around, no more bottles of beer on the wall!" 16 | -------------------------------------------------------------------------------- /solutions/ch10-nothing-new/shuffle/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | def shuffle arr 2 | # arr.sort_by(&:rand) 3 | # note the above was how Chris pine did 4 | # do it, but it no longer works, instead do the 5 | # following: 6 | arr.shuffle 7 | end 8 | 9 | # p(shuffle([1,2,3,4,5,6,7,8,9])) -------------------------------------------------------------------------------- /solutions/ch10-nothing-new/shuffle/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def shuffle arr 2 | shuf = [] 3 | 4 | while arr.length > 0 5 | # Randomly pick one element of the array. 6 | rand_index = rand(arr.length) 7 | 8 | # Now go through each item in the array, 9 | # putting them all into new_arr except for the 10 | # randomly chosen one, which goes into shuf. 11 | curr_index = 0 12 | new_arr = [] 13 | 14 | arr.each do |item| 15 | if curr_index == rand_index 16 | shuf.push item 17 | else 18 | new_arr.push item 19 | end 20 | 21 | curr_index = curr_index + 1 22 | end 23 | 24 | # Replace the original array with the new, 25 | # smaller array. 26 | arr = new_arr 27 | end 28 | 29 | shuf 30 | end 31 | 32 | # puts(shuffle([1,2,3,4,5,6,7,8,9])) 33 | -------------------------------------------------------------------------------- /solutions/ch10-nothing-new/sort/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | # well, aside from just using the built-in sort method: 2 | 3 | # The well-known quicksort algorithm. 4 | def sort arr 5 | return arr if arr.length <= 1 6 | 7 | middle = arr.pop 8 | less = arr.select{|x| x < middle} 9 | more = arr.select{|x| x >= middle} 10 | 11 | sort(less) + [middle] + sort(more) 12 | end 13 | 14 | # p(sort(['can','feel','singing','like','a','can'])) -------------------------------------------------------------------------------- /solutions/ch10-nothing-new/sort/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def sort arr 2 | rec_sort arr, [] 3 | end 4 | 5 | def rec_sort unsorted, sorted 6 | if unsorted.length <= 0 7 | return sorted 8 | end 9 | # So if we got here, then it means we still # have work to do. 10 | smallest = unsorted.pop 11 | still_unsorted = [] 12 | 13 | unsorted.each do |tested_object| 14 | if tested_object < smallest 15 | still_unsorted.push smallest 16 | smallest = tested_object 17 | else 18 | still_unsorted.push tested_object 19 | end 20 | end 21 | 22 | # Now "smallest" really does point to the 23 | # smallest element that "unsorted" contained, 24 | # and all the rest of it is in "still_unsorted". l 25 | sorted.push smallest 26 | 27 | rec_sort still_unsorted, sorted 28 | end 29 | 30 | # puts(sort([' can' ,' feel' ,' singing' ,' like' ,' a' ,' can' ])) -------------------------------------------------------------------------------- /solutions/ch11-reading-and-writing/build_a_better_playlist/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | def music_shuffle filenames 2 | songs_and_paths = filenames.map do |s| 3 | [s, s.split('/')] # [song, path] 4 | end 5 | 6 | tree = {:root => []} 7 | 8 | # put each song into the tree 9 | insert_into_tree = proc do |branch, song, path| 10 | if path.length == 0 # add to current branch 11 | branch[:root] << song 12 | else # delve deeper 13 | sub_branch = path[0] 14 | path.shift # like "pop", but pops off the front 15 | 16 | if !branch[sub_branch] 17 | branch[sub_branch] = {:root => []} 18 | end 19 | 20 | insert_into_tree[branch[sub_branch], song, path] 21 | end 22 | end 23 | 24 | songs_and_paths.each{|sp| insert_into_tree[tree, *sp]} 25 | 26 | # recursively: 27 | # - shuffle sub-branches (and root) 28 | # - weight each sub-branch (and root) 29 | # - merge (shuffle) these groups together 30 | shuffle_branch = proc do |branch| 31 | shuffled_subs = [] 32 | 33 | branch.each do |key, unshuffled| 34 | shuffled_subs << if key == :root 35 | unshuffled # At this level, these are all duplicates. 36 | else 37 | shuffle_branch[unshuffled] 38 | end 39 | end 40 | 41 | weighted_songs = [] 42 | 43 | shuffled_subs.each do |shuffled_songs| 44 | shuffled_songs.each_with_index do |song, idx| 45 | num = shuffled_songs.length.to_f 46 | weight = (idx + rand) / num 47 | weighted_songs << [song, weight] 48 | end 49 | end 50 | 51 | weighted_songs.sort_by{|s,v| v}.map{|s,v| s} 52 | end 53 | shuffle_branch[tree] 54 | end 55 | 56 | # songs = ['aa/bbb', 'aa/ccc', 'aa/ddd', 57 | # 'AAA/xxxx', 'AAA/yyyy', 'AAA/zzzz', 'foo/bar'] 58 | # puts(music_shuffle(songs)) 59 | -------------------------------------------------------------------------------- /solutions/ch11-reading-and-writing/build_a_better_playlist/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def music_shuffle filenames 2 | # We don't want a perfectly random shuffle, so let's 3 | # instead do a shuffle like card-shuffling. Let's 4 | # shuffle the "deck" twice, then cut it once. That's 5 | # not enough times to make a perfect shuffle, but it 6 | # does mix things up a bit. 7 | # Before we do anything, let's actually *sort* the 8 | # input, since we don't know how shuffled it might 9 | # already be, and we don't want it to be *too* random. 10 | filenames = filenames.sort 11 | len = filenames.length 12 | 13 | # Now we shuffle twice. 14 | 2.times do 15 | l_idx = 0 # index of next card in left pile 16 | r_idx = len/2 # index of next card in right pile 17 | shuf = [] 18 | # NOTE: If we have an odd number of "cards", 19 | # then the right pile will be larger. 20 | 21 | while shuf.length < len 22 | if shuf.length%2 == 0 23 | # take card from right pile 24 | shuf.push(filenames[r_idx]) 25 | r_idx = r_idx + 1 26 | else 27 | # take card from left pile 28 | shuf.push(filenames[l_idx]) 29 | l_idx = l_idx + 1 30 | end 31 | end 32 | 33 | filenames = shuf 34 | end 35 | # And cut the deck. 36 | arr = [] 37 | cut = rand(len) # index of card to cut at 38 | idx = 0 39 | 40 | while idx < len 41 | arr.push(filenames[(idx+cut)%len]) 42 | idx = idx + 1 43 | end 44 | 45 | arr 46 | end 47 | # songs = ['aa/bbb', 'aa/ccc', 'aa/ddd', 48 | # 'AAA/xxxx', 'AAA/yyyy', 'AAA/zzzz', 'foo/bar'] 49 | # puts(music_shuffle(songs)) 50 | -------------------------------------------------------------------------------- /solutions/ch11-reading-and-writing/build_your_own_playlist/how_you_and_i_could_do_it.rb: -------------------------------------------------------------------------------- 1 | # using the shuffle method as defined above 2 | 3 | all_oggs = shuffle(Dir['**/*.ogg']) 4 | 5 | File.open 'playlist.m3u', 'w' do |f| 6 | all_oggs.each do |ogg| 7 | f.write ogg+"\n" 8 | end 9 | end 10 | 11 | puts 'Done!' 12 | -------------------------------------------------------------------------------- /solutions/ch11-reading-and-writing/safer_picture_downloading/how_i_did_it_on_windows.rb: -------------------------------------------------------------------------------- 1 | # For Katy, with love. 2 | 3 | ### Download pictures from camera card. 4 | 5 | require 'win32ole' 6 | 7 | STDOUT.sync = true 8 | Thread.abort_on_exception = true 9 | 10 | Dir.chdir 'C:\Documents and Settings\Chris\Desktop\pictureinbox' 11 | 12 | # Always look here for pics. 13 | 14 | pic_names = Dir['!undated/**/*.{jpg,avi}'] 15 | thm_names = Dir['!undated/**/*.{thm}' ] 16 | 17 | # Scan for memory cards in the card reader. 18 | WIN32OLE.new("Scripting.FileSystemObject").Drives.each() do |x| 19 | #driveType 1 is removable disk 20 | if x.DriveType == 1 && x.IsReady 21 | pic_names += Dir[x.DriveLetter+':/**/*.{jpg,avi}'] 22 | thm_names += Dir[x.DriveLetter+':/**/*.{thm}' ] 23 | end 24 | end 25 | 26 | months = %w(jan feb mar apr may jun jul aug sep oct nov dec) 27 | 28 | encountered_error = false 29 | 30 | print "Downloading #{pic_names.size} files: " 31 | 32 | pic_names.each do |name| 33 | print '.' 34 | is_movie = (name[-3..-1].downcase == 'avi') 35 | 36 | if is_movie 37 | orientation = 0 38 | new_name = File.open(name) do |f| 39 | f.seek(0x144,IO::SEEK_SET) 40 | f.read(20) 41 | end 42 | 43 | new_name[0...3] = '%.2d' % (1 + months.index(new_name[0...3].downcase)) 44 | new_name = new_name[-4..-1] + ' ' + new_name[0...-5] 45 | else 46 | new_name, orientation = File.open(name) do |f| 47 | f.seek(0x36, IO::SEEK_SET) 48 | orientation_ = f.read(1)[0] 49 | f.seek(0xbc, IO::SEEK_SET) 50 | new_name_ = f.read(19) 51 | [new_name_, orientation_] 52 | end 53 | end 54 | 55 | [4,7,10,13,16].each {|n| new_name[n] = '.'} 56 | if new_name[0] != '2'[0] 57 | encountered_error = true 58 | puts "\n"+'ERROR: Could not process "'+name+ 59 | '" because it\'s not in the proper format!' 60 | next 61 | end 62 | 63 | save_name = new_name + (is_movie ? '.orig.avi' : '.jpg') 64 | # Make sure we don't save over another file!! 65 | while FileTest.exist? save_name 66 | new_name += 'a' 67 | save_name = new_name + (is_movie ? '.orig.avi' : '.jpg') 68 | end 69 | 70 | 71 | case orientation 72 | when 6 73 | `convert "#{name}" -rotate "90>" "#{save_name}"` 74 | File.delete name 75 | when 8 76 | `convert "#{name}" -rotate "-90>" "#{save_name}"` 77 | File.delete name 78 | else 79 | File.rename name, save_name 80 | end 81 | end 82 | 83 | print "\nDeleting #{thm_names.size} THM files: " 84 | thm_names.each do |name| 85 | print '.' 86 | File.delete name 87 | end 88 | 89 | # If something bad happened, make sure she 90 | 91 | # sees the error message before the window closes. 92 | 93 | if encountered_error 94 | puts 95 | puts "Press [Enter] to finish." 96 | puts 97 | gets 98 | end 99 | -------------------------------------------------------------------------------- /solutions/ch12-new-classes-of-objects/birthday_helper/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | # First, load in the birthdates. 2 | birth_dates = {} 3 | 4 | File.readlines('birthdates.txt').each do |line| 5 | name, date, year = line.split(',') 6 | birth_dates[name] = Time.gm(year, *(date.split)) 7 | end 8 | 9 | # Now ask the user which one they want to know. 10 | puts 'Whose birthday would you like to know?' 11 | name = gets.chomp 12 | bday = birth_dates[name] 13 | 14 | if bday == nil 15 | puts "Oooh, I don't know that one..." 16 | else 17 | now = Time.new 18 | age = now.year - bday.year 19 | 20 | if now.month > bday.month || (now.month == bday.month && now.day > bday.day) 21 | age += 1 22 | end 23 | 24 | if now.month == bday.month && now.day == bday.day 25 | puts "#{name} turns #{age} TODAY!!" 26 | else 27 | date = bday.strftime "%b %d" 28 | puts "#{name} will be #{age} on #{date}." 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /solutions/ch12-new-classes-of-objects/birthday_helper/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | # First, load in the birthdates. 2 | birth_dates = {} 3 | File.read('birthdates.txt').each_line do |line| 4 | line = line.chomp 5 | # Find the index of first comma, 6 | # so we know where the name ends. 7 | first_comma = 0 8 | while line[first_comma] != ',' && 9 | first_comma < line.length 10 | first_comma = first_comma + 1 11 | end 12 | 13 | name = line[0..(first_comma - 1)] 14 | date = line[-12..-1] 15 | birth_dates[name] = date 16 | end 17 | 18 | # Now ask the user which one they want to know. 19 | puts 'Whose birthday would you like to know?' 20 | name = gets.chomp 21 | date = birth_dates[name] 22 | 23 | if date == nil 24 | puts "Oooh, I don't know that one..." 25 | else 26 | puts date[0..5] 27 | end 28 | -------------------------------------------------------------------------------- /solutions/ch12-new-classes-of-objects/happy_birthday/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | puts 'Hey, when were you born? (Please use YYYYMMDD format.)' 2 | input = gets.chomp 3 | 4 | b_year = input[0..3].to_i 5 | b_month = input[4..5].to_i 6 | b_day = input[6..7].to_i 7 | 8 | t = Time.new 9 | 10 | t_year = t.year 11 | t_month = t.month 12 | t_day = t.day 13 | 14 | age = t_year - b_year 15 | 16 | if t_month < b_month || (t_month == b_month && t_day < b_day) 17 | age -= 1 18 | end 19 | 20 | if t_month == b_month && t_day == b_day 21 | puts 'HAPPY BIRTHDAY!!' 22 | end 23 | 24 | age.times { puts 'SPANK!' } 25 | -------------------------------------------------------------------------------- /solutions/ch12-new-classes-of-objects/happy_birthday/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | puts 'What year were you born?' 2 | b_year = gets.chomp.to_i 3 | 4 | puts 'What month were you born? (1-12)' 5 | b_month = gets.chomp.to_i 6 | 7 | puts 'What day of the month were you born?' 8 | b_day = gets.chomp.to_i 9 | 10 | b = Time.local(b_year, b_month, b_day) 11 | t = Time.new 12 | 13 | age = 1 14 | 15 | while Time.local(b_year + age, b_month, b_day) <= t 16 | puts 'SPANK!' 17 | age = age + 1 18 | end 19 | -------------------------------------------------------------------------------- /solutions/ch12-new-classes-of-objects/one_billion_seconds/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | puts(Time.gm(1976, 8, 3, 13, 31) + 10**9) -------------------------------------------------------------------------------- /solutions/ch12-new-classes-of-objects/party_like_its_roman_to_integer_mcmxcix.rb/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | def roman_to_integer roman 2 | digit_vals = {'i' => 1, 3 | 'v' => 5, 4 | 'x' => 10, 5 | 'l' => 50, 6 | 'c' => 100, 7 | 'd' => 500, 8 | 'm' => 1000} 9 | total = 0 10 | prev = 0 11 | roman.reverse.each_char do |c_or_C| 12 | c = c_or_C.downcase 13 | val = digit_vals[c] 14 | if !val 15 | puts 'This is not a valid roman numeral!' 16 | return 17 | end 18 | 19 | if val < prev 20 | val *= -1 21 | else 22 | prev = val 23 | end 24 | 25 | total += val 26 | end 27 | 28 | total 29 | end 30 | 31 | # puts(roman_to_integer('mcmxcix')) 32 | # puts(roman_to_integer('CCCLXV')) 33 | -------------------------------------------------------------------------------- /solutions/ch12-new-classes-of-objects/party_like_its_roman_to_integer_mcmxcix.rb/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def roman_to_integer roman 2 | digit_vals = {'i' => 1, 3 | 'v' => 5, 4 | 'x' => 10, 5 | 'l' => 50, 6 | 'c' => 100, 7 | 'd' => 500, 8 | 'm' => 1000} 9 | total = 0 10 | prev = 0 11 | index = roman.length - 1 12 | while index >= 0 13 | c = roman[index].downcase 14 | index = index - 1 15 | val = digit_vals[c] 16 | if !val 17 | puts 'This is not a valid roman numeral!' 18 | return 19 | end 20 | 21 | if val < prev 22 | val = val * -1 23 | else 24 | prev = val 25 | end 26 | total = total + val 27 | end 28 | 29 | total 30 | end 31 | 32 | # puts(roman_to_integer('mcmxcix')) 33 | # puts(roman_to_integer('CCCLXV')) 34 | -------------------------------------------------------------------------------- /solutions/ch13-creating-new-classes/extend_the_built_in_classes/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | # class Array 2 | # def shuffle 3 | # sort_by{rand} # "self" is implied, remember? 4 | # end 5 | # end 6 | # note modern ruby has already has a built in shuffle method 7 | 8 | class Integer 9 | def factorial 10 | raise 'Must not use negative integer' if self < 0 11 | (self <= 1) ? 1 : self * (self-1).factorial 12 | end 13 | def to_roman 14 | # I chose old-school roman numerals just to save space. 15 | raise 'Must use positive integer' if self <= 0 16 | roman = '' 17 | roman << 'M' * (self / 1000) 18 | roman << 'D' * (self % 1000 / 500) 19 | roman << 'C' * (self % 500 / 100) 20 | roman << 'L' * (self % 100 / 50) 21 | roman << 'X' * (self % 50 / 10) 22 | roman << 'V' * (self % 10 / 5) 23 | roman << 'I' * (self % 5 / 1) 24 | roman 25 | end 26 | end 27 | # Get ready for the pure awesome... 28 | # p 7.factorial.to_roman.split(//).shuffle 29 | -------------------------------------------------------------------------------- /solutions/ch13-creating-new-classes/extend_the_built_in_classes/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | # class Array 2 | # def shuffle 3 | # arr = self 4 | # # Now we can just copy the old shuffle method. 5 | # shuf = [] 6 | 7 | # while arr.length > 0 8 | # # Randomly pick one element of the array. 9 | # rand_index = rand(arr.length) 10 | 11 | # # Now go through each item in the array, 12 | # # putting them all into new_arr except for 13 | # # the randomly chosen one, which goes into 14 | # # shuf. 15 | # curr_index = 0 16 | # new_arr = [] 17 | # arr.each do |item| 18 | # if curr_index == rand_index 19 | # shuf.push item 20 | # else 21 | # new_arr.push item 22 | # end 23 | 24 | # curr_index = curr_index + 1 25 | # end 26 | # # Replace the original array with the new, 27 | # # smaller array. 28 | 29 | # arr = new_arr 30 | # end 31 | # shuf 32 | # end 33 | # end 34 | 35 | # note modern ruby has already has a built in shuffle method 36 | 37 | class Integer 38 | def factorial 39 | if self <= 1 40 | 1 41 | else 42 | self * (self-1).factorial 43 | end 44 | end 45 | def to_roman 46 | # I chose old-school roman numerals just to save space. 47 | roman = '' 48 | 49 | roman = roman + 'M' * (self / 1000) 50 | roman = roman + 'D' * (self % 1000 / 500) 51 | roman = roman + 'C' * (self % 500 / 100) 52 | roman = roman + 'L' * (self % 100 / 50) 53 | roman = roman + 'X' * (self % 50 / 10) 54 | roman = roman + 'V' * (self % 10 / 5) 55 | roman = roman + 'I' * (self % 5 / 1) 56 | 57 | roman 58 | end 59 | end 60 | # puts [1,2,3,4,5].shuffle 61 | # puts 7.factorial 62 | # puts 73.to_roman 63 | -------------------------------------------------------------------------------- /solutions/ch13-creating-new-classes/interactive_baby_dragon/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | # using the Dragon class from the chapter 2 | puts 'What would you like to name your baby dragon?' 3 | name = gets.chomp 4 | pet = Dragon.new name 5 | obj = Object.new # just a blank, dummy object 6 | 7 | while true 8 | puts 9 | puts 'commands: feed, toss, walk, rock, put_to_bed, exit' 10 | command = gets.chomp 11 | if command == 'exit' 12 | exit 13 | elsif pet.respond_to?(command) && !obj.respond_to?(command) 14 | # I only want to accept methods that dragons have, 15 | # but that regular objects *don't* have. 16 | pet.send command 17 | else 18 | puts 'Huh? Please type one of the commands.' 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /solutions/ch13-creating-new-classes/interactive_baby_dragon/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | # using the Dragon class from the chapter 2 | puts 'What would you like to name your baby dragon?' 3 | name = gets.chomp 4 | pet = Dragon.new name 5 | 6 | while true 7 | puts 8 | puts 'commands: feed, toss, walk, rock, put to bed, exit' 9 | command = gets.chomp 10 | 11 | if command == 'exit' 12 | exit 13 | elsif command == 'feed' 14 | pet.feed 15 | elsif command == 'toss' 16 | pet.toss 17 | elsif command == 'walk' 18 | pet.walk 19 | elsif command == 'rock' 20 | pet.rock 21 | elsif command == 'put to bed' 22 | pet.put_to_bed 23 | else 24 | puts 'Huh? Please type one of the commands.' 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /solutions/ch13-creating-new-classes/orange_tree/how_you_and_i_could_do_it.rb: -------------------------------------------------------------------------------- 1 | # note we have added a rounding operation on the height to ensure 2 | # the output is sensible in terms of decimal places 3 | 4 | class OrangeTree 5 | def initialize 6 | @height = 0 7 | @orange_count = 0 8 | @alive = true 9 | end 10 | 11 | def height 12 | if @alive 13 | @height.round(1) 14 | else 15 | 'A dead tree is not very tall. :(' 16 | end 17 | end 18 | 19 | def count_the_oranges 20 | if @alive 21 | @orange_count 22 | else 23 | 'A dead tree has no oranges. :(' 24 | end 25 | end 26 | 27 | def one_year_passes 28 | if @alive 29 | @height = @height + 0.4 30 | @orange_count = 0 # old oranges fall off 31 | if @height > 10 && rand(2) > 0 32 | # tree dies 33 | @alive = false 34 | 'Oh, no! The tree is too old, and has died. :(' 35 | elsif @height > 2 36 | # new oranges grow 37 | @orange_count = (@height * 15 - 25).to_i 38 | "This year your tree grew to #{@height.round(1)}m tall," + 39 | " and produced #{@orange_count} oranges." 40 | else 41 | "This year your tree grew to #{@height.round(1)}m tall," + 42 | " but is still too young to bear fruit." 43 | end 44 | else 45 | 'A year later, the tree is still dead. :(' 46 | end 47 | end 48 | 49 | def pick_an_orange 50 | if @alive 51 | if @orange_count > 0 52 | @orange_count = @orange_count - 1 53 | 'You pick a juicy, delicious orange!' 54 | else 55 | 'You search every branch, but find no oranges.' 56 | end 57 | else 58 | 'A dead tree has nothing to pick. :(' 59 | end 60 | end 61 | end 62 | 63 | # ot = OrangeTree.new 64 | # 23.times do 65 | # ot.one_year_passes 66 | # end 67 | 68 | # puts(ot.one_year_passes) 69 | # puts(ot.count_the_oranges) 70 | # puts(ot.height) 71 | # puts(ot.one_year_passes) 72 | # puts(ot.one_year_passes) 73 | # puts(ot.one_year_passes) 74 | # puts(ot.one_year_passes) 75 | # puts(ot.one_year_passes) 76 | # puts(ot.height) 77 | # puts(ot.count_the_oranges) 78 | # puts(ot.pick_an_orange) 79 | -------------------------------------------------------------------------------- /solutions/ch14-blocks-and-procs/better_program_logger/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | $logger_depth = 0 2 | 3 | def log desc, &block 4 | prefix = ' '*$logger_depth 5 | puts prefix+"Beginning #{desc.inspect}..." 6 | $logger_depth += 1 7 | result = block[] 8 | $logger_depth -= 1 9 | puts prefix+"...#{desc.inspect} finished, returning: #{result}" 10 | end 11 | 12 | log 'outer block' do 13 | log 'some little block' do 14 | log 'teeny-tiny block' do 15 | 'lOtS oF lOVe'.downcase 16 | end 17 | 7 * 3 * 2 18 | end 19 | 20 | log 'yet another block' do 21 | '!doof naidnI evol I'.reverse 22 | end 23 | '0' == "0" 24 | end 25 | -------------------------------------------------------------------------------- /solutions/ch14-blocks-and-procs/better_program_logger/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | $logger_depth = 0 2 | 3 | def log desc, &block 4 | prefix = ' '*$logger_depth 5 | 6 | puts prefix + 'Beginning "' + desc + '"...' 7 | 8 | $logger_depth = $logger_depth + 1 9 | 10 | result = block.call 11 | 12 | $logger_depth = $logger_depth - 1 13 | puts prefix + '..."' + desc + '" finished, returning: ' + result.to_s 14 | end 15 | 16 | log 'outer block' do 17 | log 'some little block' do 18 | log 'teeny-tiny block' do 19 | 'lOtS oF lOVe'.downcase 20 | end 21 | 22 | 7 * 3 * 2 23 | end 24 | 25 | log 'yet another block' do 26 | '!doof naidnI evol I'.reverse 27 | end 28 | 29 | '0' == "0" 30 | end 31 | -------------------------------------------------------------------------------- /solutions/ch14-blocks-and-procs/even_better_profiling/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | $OPT_PROFILING_ON = false 2 | 3 | def profile block_description, &block 4 | if $OPT_PROFILING_ON 5 | start_time = Time.new 6 | block[] 7 | duration = Time.new - start_time 8 | puts "#{block_description}: #{duration} seconds" 9 | else 10 | block[] 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /solutions/ch14-blocks-and-procs/even_better_profiling/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def profile block_description, &block 2 | # To turn profiling on/off, set this 3 | # to true/false. 4 | profiling_on = false 5 | if profiling_on 6 | start_time = Time.new 7 | block.call 8 | 9 | duration = Time.new - start_time 10 | puts "#{block_description}: #{duration} seconds" 11 | else 12 | block.call 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /solutions/ch14-blocks-and-procs/grandfather_clock/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | def grandfather_clock &block 2 | hour = (Time.new.hour + 11)%12 + 1 3 | 4 | hour.times(&block) 5 | end 6 | 7 | grandfather_clock { puts 'DONG!' } 8 | -------------------------------------------------------------------------------- /solutions/ch14-blocks-and-procs/grandfather_clock/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def grandfather_clock &block 2 | hour = Time.new.hour 3 | 4 | if hour >= 13 5 | hour = hour - 12 6 | end 7 | 8 | if hour == 0 9 | hour = 12 10 | end 11 | 12 | hour.times do 13 | block.call 14 | end 15 | end 16 | grandfather_clock do 17 | puts 'DONG!' 18 | end 19 | -------------------------------------------------------------------------------- /solutions/ch14-blocks-and-procs/program_logger/how_i_would_do_it.rb: -------------------------------------------------------------------------------- 1 | def log desc, &block 2 | puts "Beginning #{desc.inspect}..." 3 | result = block[] 4 | puts "...#{desc.inspect} finished, returning: #{result}" 5 | end 6 | 7 | log 'outer block' do 8 | log 'some little block' do 9 | 1**1 + 2**2 10 | end 11 | 12 | log 'yet another block' do 13 | '!doof iahT ekil I'.reverse 14 | end 15 | 16 | '0' == 0 17 | end 18 | -------------------------------------------------------------------------------- /solutions/ch14-blocks-and-procs/program_logger/how_you_could_do_it.rb: -------------------------------------------------------------------------------- 1 | def log desc, &block 2 | puts 'Beginning "' + desc + '"...' 3 | result = block.call 4 | puts '..."' + desc + '" finished, returning: ' + result.to_s 5 | end 6 | 7 | log 'outer block' do 8 | log 'some little block' do 9 | 1**1 + 2**2 10 | end 11 | 12 | log 'yet another block' do 13 | '!doof iahT ekil I'.reverse 14 | end 15 | 16 | '0' == 0 17 | end 18 | -------------------------------------------------------------------------------- /spec/ch09/ask_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'ask' do 2 | before do 3 | stub(:puts).with('anything') 4 | stub(:puts).with('Please answer "yes" or "no".') 5 | stub(:gets).and_return 'yes' 6 | allow_any_instance_of(Kernel).to receive(:gets).and_return 'yes' 7 | require './ch09-writing-your-own-methods/ask.rb' 8 | end 9 | it 'returns true when you say yes' do 10 | expect(ask('anything')).to be true 11 | end 12 | it 'returns false when you say no' do 13 | allow_any_instance_of(Kernel).to receive(:gets).and_return 'no' 14 | expect(ask('anything')).to be false 15 | end 16 | end -------------------------------------------------------------------------------- /spec/ch09/old_school_roman_numerals_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'old school roman numerals' do 2 | numerals = ['I','II','III','IIII','V','VI','VII','VIII','VIIII','X', 3 | 'XI','XII','XIII','XIIII','XV','XVI','XVII','XVIII','XVIIII','XX'] 4 | numerals.each.with_index do |numeral, i| 5 | it "translates #{i+1} to #{numeral}" do 6 | allow(STDOUT).to receive(:puts) 7 | require './ch09-writing-your-own-methods/old_school_roman_numerals.rb' 8 | expect(old_roman_numeral(i+1)).to eq numeral 9 | end 10 | end 11 | l_numerals = ['L','LI','LII','LIII','LIIII','LV','LVI','LVII','LVIII','LVIIII', 12 | 'LX','LXI','LXII','LXIII','LXIIII','LXV','LXVI','LXVII','LXVIII','LXVIIII'] 13 | l_numerals.each.with_index do |numeral, i| 14 | it "translates #{i+50} to #{numeral}" do 15 | allow(STDOUT).to receive(:puts) 16 | require './ch09-writing-your-own-methods/old_school_roman_numerals.rb' 17 | expect(old_roman_numeral(i+50)).to eq numeral 18 | end 19 | end 20 | c_numerals = ['C','CI','CII','CIII','CIIII','CV','CVI','CVII','CVIII','CVIIII', 21 | 'CX','CXI','CXII','CXIII','CXIIII','CXV','CXVI','CXVII','CXVIII','CXVIIII'] 22 | c_numerals.each.with_index do |numeral, i| 23 | it "translates #{i+100} to #{numeral}" do 24 | allow(STDOUT).to receive(:puts) 25 | require './ch09-writing-your-own-methods/old_school_roman_numerals.rb' 26 | expect(old_roman_numeral(i+100)).to eq numeral 27 | end 28 | end 29 | d_numerals = ['D','DI','DII','DIII','DIIII','DV','DVI','DVII','DVIII','DVIIII', 30 | 'DX','DXI','DXII','DXIII','DXIIII','DXV','DXVI','DXVII','DXVIII','DXVIIII'] 31 | d_numerals.each.with_index do |numeral, i| 32 | it "translates #{i+500} to #{numeral}" do 33 | allow(STDOUT).to receive(:puts) 34 | require './ch09-writing-your-own-methods/old_school_roman_numerals.rb' 35 | expect(old_roman_numeral(i+500)).to eq numeral 36 | end 37 | end 38 | m_numerals = ['M','MI','MII','MIII','MIIII','MV','MVI','MVII','MVIII','MVIIII', 39 | 'MX','MXI','MXII','MXIII','MXIIII','MXV','MXVI','MXVII','MXVIII','MXVIIII'] 40 | m_numerals.each.with_index do |numeral, i| 41 | it "translates #{i+1000} to #{numeral}" do 42 | allow(STDOUT).to receive(:puts) 43 | require './ch09-writing-your-own-methods/old_school_roman_numerals.rb' 44 | expect(old_roman_numeral(i+1000)).to eq numeral 45 | end 46 | end 47 | end -------------------------------------------------------------------------------- /spec/ch09/roman_numeral_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'roman numerals' do 2 | numerals = ['I','II','III','IV','V','VI','VII','VIII','IX','X', 3 | 'XI','XII','XIII','XIV','XV','XVI','XVII','XVIII','XIX','XX'] 4 | numerals.each.with_index do |numeral, i| 5 | it "translates #{i+1} to #{numeral}" do 6 | allow(STDOUT).to receive(:puts) 7 | require './ch09-writing-your-own-methods/roman_numerals.rb' 8 | expect(roman_numeral(i+1)).to eq numeral 9 | end 10 | end 11 | l_numerals = ['L','LI','LII','LIII','LIV','LV','LVI','LVII','LVIII','LIX', 12 | 'LX','LXI','LXII','LXIII','LXIV','LXV','LXVI','LXVII','LXVIII','LXIX'] 13 | l_numerals.each.with_index do |numeral, i| 14 | it "translates #{i+50} to #{numeral}" do 15 | allow(STDOUT).to receive(:puts) 16 | require './ch09-writing-your-own-methods/roman_numerals.rb' 17 | expect(roman_numeral(i+50)).to eq numeral 18 | end 19 | end 20 | c_numerals = ['C','CI','CII','CIII','CIV','CV','CVI','CVII','CVIII','CIX', 21 | 'CX','CXI','CXII','CXIII','CXIV','CXV','CXVI','CXVII','CXVIII','CXIX'] 22 | c_numerals.each.with_index do |numeral, i| 23 | it "translates #{i+100} to #{numeral}" do 24 | allow(STDOUT).to receive(:puts) 25 | require './ch09-writing-your-own-methods/roman_numerals.rb' 26 | expect(roman_numeral(i+100)).to eq numeral 27 | end 28 | end 29 | d_numerals = ['D','DI','DII','DIII','DIV','DV','DVI','DVII','DVIII','DIX', 30 | 'DX','DXI','DXII','DXIII','DXIV','DXV','DXVI','DXVII','DXVIII','DXIX'] 31 | d_numerals.each.with_index do |numeral, i| 32 | it "translates #{i+500} to #{numeral}" do 33 | allow(STDOUT).to receive(:puts) 34 | require './ch09-writing-your-own-methods/roman_numerals.rb' 35 | expect(roman_numeral(i+500)).to eq numeral 36 | end 37 | end 38 | m_numerals = ['M','MI','MII','MIII','MIV','MV','MVI','MVII','MVIII','MIX', 39 | 'MX','MXI','MXII','MXIII','MXIV','MXV','MXVI','MXVII','MXVIII','MXIX'] 40 | m_numerals.each.with_index do |numeral, i| 41 | it "translates #{i+1000} to #{numeral}" do 42 | allow(STDOUT).to receive(:puts) 43 | require './ch09-writing-your-own-methods/roman_numerals.rb' 44 | expect(roman_numeral(i+1000)).to eq numeral 45 | end 46 | end 47 | end -------------------------------------------------------------------------------- /spec/ch10/dictionary_sort_spec.rb: -------------------------------------------------------------------------------- 1 | require './ch10-nothing-new/dictionary_sort.rb' 2 | 3 | describe 'dictionary sort' do 4 | it 'sorts dictionary style' do 5 | words = ['can','feel','singing.','like','A','can'] 6 | expect(dictionary_sort(words).join(' ')).to eq 'A can can feel like singing.' 7 | end 8 | end -------------------------------------------------------------------------------- /spec/ch10/english_number_spec.rb: -------------------------------------------------------------------------------- 1 | require './ch10-nothing-new/english_number.rb' 2 | 3 | describe 'english number' do 4 | describe 'translates numbers to english text' do 5 | 6 | { 7 | 0 => 'zero', 8 | 9 => 'nine', 9 | 10 => 'ten', 10 | 11 => 'eleven', 11 | 17 => 'seventeen', 12 | 32 => 'thirty-two', 13 | 88 => 'eighty-eight', 14 | 99 => 'ninety-nine', 15 | 100 => 'one hundred', 16 | 101 => 'one hundred one', 17 | 234 => 'two hundred thirty-four', 18 | 3211 => 'three thousand two hundred eleven', 19 | 999999 => 'nine hundred ninety-nine thousand nine hundred ninety-nine', 20 | 1000000000000 => 'one trillion', 21 | 109238745102938560129834709285360238475982374561034 => 'one hundred nine quindecillion two hundred thirty-eight quattuordecillion seven hundred forty-five tredecillion one hundred two duodecillion nine hundred thirty-eight undecillion five hundred sixty decillion one hundred twenty-nine nonillion eight hundred thirty-four octillion seven hundred nine septillion two hundred eighty-five sextillion three hundred sixty quintillion two hundred thirty-eight quadrillion four hundred seventy-five trillion nine hundred eighty-two billion three hundred seventy-four million five hundred sixty-one thousand thirty-four' 22 | }.each do |number, english| 23 | it "translates #{number} to #{english}" do 24 | expect(english_number(number)).to eq english 25 | end 26 | end 27 | end 28 | 29 | end -------------------------------------------------------------------------------- /spec/ch10/ninety_nine_bottles_of_beer_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'ninety nine bottles of beer' do 2 | 3 | end -------------------------------------------------------------------------------- /spec/ch10/shuffle_spec.rb: -------------------------------------------------------------------------------- 1 | require './ch10-nothing-new/shuffle.rb' 2 | 3 | describe 'shuffle' do 4 | 5 | let(:array){[1,2,3,4,5,6,7,8,9]} 6 | let(:shuffled){[3, 7, 2, 6, 9, 4, 8, 1, 5]} 7 | 8 | before :each do 9 | srand(67809) 10 | allow(STDOUT).to receive(:puts) 11 | 12 | allow(array).to receive(:shuffle).and_return shuffled 13 | end 14 | 15 | it 'maintains all the same elements' do 16 | expect(Set.new(shuffle(array))).to eq Set.new(shuffled) 17 | end 18 | 19 | it 'creates a different order' do 20 | expect(shuffle(array)).not_to eq array 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/ch10/sort_spec.rb: -------------------------------------------------------------------------------- 1 | require './ch10-nothing-new/sort.rb' 2 | 3 | describe 'sort' do 4 | it 'sorts' do 5 | allow(STDOUT).to receive(:puts) 6 | expect(sort(['can','feel','singing','like','a','can'])).to eq ['a', 'can', 'can', 'feel', 'like', 'singing'] 7 | end 8 | end -------------------------------------------------------------------------------- /spec/ch11/build_a_better_playlist_spec.rb: -------------------------------------------------------------------------------- 1 | # instructions are not very clear so difficult to test. needs further 2 | # investigation. 3 | -------------------------------------------------------------------------------- /spec/ch12/roman_to_integer_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'roman to integer numerals' do 2 | numerals = ['i','ii','iii','iv','v','vi','vii','viii','ix','x', 3 | 'xi','xii','xiii','xiv','xv','xvi','xvii','xviii','xix','xx'] 4 | numerals.each.with_index do |numeral, i| 5 | it "translates '#{numeral}' to '#{i+1}'" do 6 | allow(STDOUT).to receive(:puts) 7 | require './ch12-new-classes-of-objects/party_like_its_roman_to_integer_mcmxcix.rb' 8 | expect(roman_to_integer(numeral)).to eq i+1 9 | end 10 | end 11 | it "translates 'mcmxcix' to '1999'" do 12 | allow(STDOUT).to receive(:puts) 13 | require './ch12-new-classes-of-objects/party_like_its_roman_to_integer_mcmxcix.rb' 14 | expect(roman_to_integer('mcmxcix')).to eq 1999 15 | end 16 | it "translates 'CCCLXV' to '365'" do 17 | allow(STDOUT).to receive(:puts) 18 | require './ch12-new-classes-of-objects/party_like_its_roman_to_integer_mcmxcix.rb' 19 | expect(roman_to_integer('CCCLXV')).to eq 365 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/ch13/extend_the_built_in_classes_spec.rb: -------------------------------------------------------------------------------- 1 | describe Integer do 2 | 3 | before do 4 | allow(STDOUT).to receive(:puts) 5 | require './ch13-creating-new-classes/extend_built_in_classes.rb' 6 | end 7 | 8 | it 'calculates factorial' do 9 | expect(7.factorial).to eq 5040 10 | end 11 | 12 | it 'converts to old school roman numerals' do 13 | expect(73.to_roman).to eq 'LXXIII' 14 | end 15 | 16 | context 'more extensive test of roman conversion' do 17 | 18 | numerals = ['I','II','III','IIII','V','VI','VII','VIII','VIIII','X', 19 | 'XI','XII','XIII','XIIII','XV','XVI','XVII','XVIII','XVIIII','XX'] 20 | numerals.each.with_index do |numeral, i| 21 | it "translates #{i+1} to #{numeral}" do 22 | expect((i+1).to_roman).to eq numeral 23 | end 24 | end 25 | l_numerals = ['L','LI','LII','LIII','LIIII','LV','LVI','LVII','LVIII','LVIIII', 26 | 'LX','LXI','LXII','LXIII','LXIIII','LXV','LXVI','LXVII','LXVIII','LXVIIII'] 27 | l_numerals.each.with_index do |numeral, i| 28 | it "translates #{i+50} to #{numeral}" do 29 | expect((i+50).to_roman).to eq numeral 30 | end 31 | end 32 | c_numerals = ['C','CI','CII','CIII','CIIII','CV','CVI','CVII','CVIII','CVIIII', 33 | 'CX','CXI','CXII','CXIII','CXIIII','CXV','CXVI','CXVII','CXVIII','CXVIIII'] 34 | c_numerals.each.with_index do |numeral, i| 35 | it "translates #{i+100} to #{numeral}" do 36 | expect((i+100).to_roman).to eq numeral 37 | end 38 | end 39 | d_numerals = ['D','DI','DII','DIII','DIIII','DV','DVI','DVII','DVIII','DVIIII', 40 | 'DX','DXI','DXII','DXIII','DXIIII','DXV','DXVI','DXVII','DXVIII','DXVIIII'] 41 | d_numerals.each.with_index do |numeral, i| 42 | it "translates #{i+500} to #{numeral}" do 43 | expect((i+500).to_roman).to eq numeral 44 | end 45 | end 46 | m_numerals = ['M','MI','MII','MIII','MIIII','MV','MVI','MVII','MVIII','MVIIII', 47 | 'MX','MXI','MXII','MXIII','MXIIII','MXV','MXVI','MXVII','MXVIII','MXVIIII'] 48 | m_numerals.each.with_index do |numeral, i| 49 | it "translates #{i+1000} to #{numeral}" do 50 | expect((i+1000).to_roman).to eq numeral 51 | end 52 | end 53 | 54 | end 55 | 56 | end 57 | -------------------------------------------------------------------------------- /spec/ch13/orange_tree_spec.rb: -------------------------------------------------------------------------------- 1 | describe 'OrangeTree' do 2 | let(:ot) { OrangeTree.new } 3 | before do 4 | allow(STDOUT).to receive(:puts) 5 | require './ch13-creating-new-classes/orange_tree.rb' 6 | 7 | 23.times do 8 | ot.one_year_passes 9 | end 10 | end 11 | 12 | it 'reports height and number of oranges' do 13 | expect(ot.one_year_passes).to eq 'This year your tree grew to 9.6m tall, and produced 119 oranges.' 14 | expect(ot.count_the_oranges).to eq 119 15 | expect(ot.height).to eq 9.6 16 | expect(ot.one_year_passes).to eq 'This year your tree grew to 10.0m tall, and produced 125 oranges.' 17 | expect(ot.one_year_passes).to eq 'Oh, no! The tree is too old, and has died. :(' 18 | expect(ot.one_year_passes).to eq 'A year later, the tree is still dead. :(' 19 | expect(ot.one_year_passes).to eq 'A year later, the tree is still dead. :(' 20 | expect(ot.one_year_passes).to eq 'A year later, the tree is still dead. :(' 21 | expect(ot.height).to eq 'A dead tree is not very tall. :(' 22 | expect(ot.count_the_oranges).to eq 'A dead tree has no oranges. :(' 23 | expect(ot.pick_an_orange).to eq 'A dead tree has nothing to pick. :(' 24 | end 25 | 26 | end -------------------------------------------------------------------------------- /spec/ch14/better_program_logger_spec.rb: -------------------------------------------------------------------------------- 1 | require './ch14-blocks-and-procs/better_program_logger.rb' 2 | 3 | describe 'program logger' do 4 | it 'logs' do 5 | expect(STDOUT).to receive(:puts).with 'Beginning "outer block"...' 6 | expect(STDOUT).to receive(:puts).with ' Beginning "some little block"...' 7 | expect(STDOUT).to receive(:puts).with ' Beginning "teeny-tiny block"...' 8 | expect(STDOUT).to receive(:puts).with ' ..."teeny-tiny block" finished, returning: lots of love' 9 | expect(STDOUT).to receive(:puts).with ' ..."some little block" finished, returning: 42' 10 | expect(STDOUT).to receive(:puts).with ' Beginning "yet another block"...' 11 | expect(STDOUT).to receive(:puts).with ' ..."yet another block" finished, returning: I love Indian food!' 12 | expect(STDOUT).to receive(:puts).with '..."outer block" finished, returning: true' 13 | 14 | better_log 'outer block' do 15 | better_log 'some little block' do 16 | better_log 'teeny-tiny block' do 17 | 'lOtS oF lOVe'.downcase 18 | end 19 | 7 * 3 * 2 20 | end 21 | 22 | better_log 'yet another block' do 23 | '!doof naidnI evol I'.reverse 24 | end 25 | '0' == "0" 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/ch14/even_better_profiling_spec.rb: -------------------------------------------------------------------------------- 1 | require './ch14-blocks-and-procs/even_better_profiling.rb' 2 | 3 | describe 'even better profiling' do 4 | it 'displays time of an activity' do 5 | expect(Time).to receive(:new).and_return(10); 6 | expect(Time).to receive(:new).and_return(20); 7 | block_description = 'test' 8 | $OPT_PROFILING_ON = true 9 | expect(STDOUT).to receive(:puts).with "#{block_description}: 10 seconds" 10 | profile(block_description) { 2 + 3 } 11 | end 12 | end -------------------------------------------------------------------------------- /spec/ch14/grandfather_clock_spec.rb: -------------------------------------------------------------------------------- 1 | require './ch14-blocks-and-procs/grandfather_clock.rb' 2 | 3 | describe 'grandfather clock' do 4 | it 'dongs or does whatever you put in the block' do 5 | allow(Time).to receive_message_chain('new.hour') { 4 } 6 | expect(STDOUT).to receive(:puts).with('DONG!').exactly(4).times 7 | grandfather_clock do 8 | puts 'DONG!' 9 | end 10 | end 11 | end -------------------------------------------------------------------------------- /spec/ch14/program_logger_spec.rb: -------------------------------------------------------------------------------- 1 | require './ch14-blocks-and-procs/program_logger.rb' 2 | 3 | describe 'program logger' do 4 | it 'logs' do 5 | expect(STDOUT).to receive(:puts).with 'Beginning "outer block"...' 6 | expect(STDOUT).to receive(:puts).with 'Beginning "some little block"...' 7 | expect(STDOUT).to receive(:puts).with '..."some little block" finished, returning: 5' 8 | expect(STDOUT).to receive(:puts).with 'Beginning "yet another block"...' 9 | expect(STDOUT).to receive(:puts).with '..."yet another block" finished, returning: I like Thai food!' 10 | expect(STDOUT).to receive(:puts).with '..."outer block" finished, returning: false' 11 | 12 | program_log 'outer block' do 13 | program_log 'some little block' do 14 | 1**1 + 2**2 15 | end 16 | 17 | program_log 'yet another block' do 18 | '!doof iahT ekil I'.reverse 19 | end 20 | 21 | '0' == 0 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | # 7 | # Given that it is always loaded, you are encouraged to keep this file as 8 | # light-weight as possible. Requiring heavyweight dependencies from this file 9 | # will add to the boot time of your test suite on EVERY test run, even for an 10 | # individual file that may not need all of that loaded. Instead, consider making 11 | # a separate helper file that requires the additional dependencies and performs 12 | # the additional setup, and require it from the spec files that actually need 13 | # it. 14 | # 15 | # The `.rspec` file also contains a few flags that are not defaults but that 16 | # users commonly want. 17 | # 18 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 19 | RSpec.configure do |config| 20 | # rspec-expectations config goes here. You can use an alternate 21 | # assertion/expectation library such as wrong or the stdlib/minitest 22 | # assertions if you prefer. 23 | config.expect_with :rspec do |expectations| 24 | # This option will default to `true` in RSpec 4. It makes the `description` 25 | # and `failure_message` of custom matchers include text for helper methods 26 | # defined using `chain`, e.g.: 27 | # be_bigger_than(2).and_smaller_than(4).description 28 | # # => "be bigger than 2 and smaller than 4" 29 | # ...rather than: 30 | # # => "be bigger than 2" 31 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 32 | end 33 | 34 | # rspec-mocks config goes here. You can use an alternate test double 35 | # library (such as bogus or mocha) by changing the `mock_with` option here. 36 | config.mock_with :rspec do |mocks| 37 | # Prevents you from mocking or stubbing a method that does not exist on 38 | # a real object. This is generally recommended, and will default to 39 | # `true` in RSpec 4. 40 | mocks.verify_partial_doubles = true 41 | mocks.syntax = [:should, :expect] 42 | end 43 | 44 | # The settings below are suggested to provide a good initial experience 45 | # with RSpec, but feel free to customize to your heart's content. 46 | =begin 47 | # These two settings work together to allow you to limit a spec run 48 | # to individual examples or groups you care about by tagging them with 49 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 50 | # get run. 51 | config.filter_run :focus 52 | config.run_all_when_everything_filtered = true 53 | 54 | # Limits the available syntax to the non-monkey patched syntax that is 55 | # recommended. For more details, see: 56 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 57 | # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 58 | # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching 59 | config.disable_monkey_patching! 60 | 61 | # This setting enables warnings. It's recommended, but in some cases may 62 | # be too noisy due to issues in dependencies. 63 | config.warnings = true 64 | 65 | # Many RSpec users commonly either run the entire suite or an individual 66 | # file, and it's useful to allow more verbose output when running an 67 | # individual spec file. 68 | if config.files_to_run.one? 69 | # Use the documentation formatter for detailed output, 70 | # unless a formatter has already been configured 71 | # (e.g. via a command-line flag). 72 | config.default_formatter = 'doc' 73 | end 74 | 75 | # Print the 10 slowest examples and example groups at the 76 | # end of the spec run, to help surface which specs are running 77 | # particularly slow. 78 | config.profile_examples = 10 79 | 80 | # Run specs in random order to surface order dependencies. If you find an 81 | # order dependency and want to debug it, you can fix the order by providing 82 | # the seed, which is printed after each run. 83 | # --seed 1234 84 | config.order = :random 85 | 86 | # Seed global randomization in this process using the `--seed` CLI option. 87 | # Setting this allows you to use `--seed` to deterministically reproduce 88 | # test failures related to randomization by passing the same `--seed` value 89 | # as the one that triggered the failure. 90 | Kernel.srand config.seed 91 | =end 92 | end 93 | --------------------------------------------------------------------------------