├── 10 ├── Rakefile ├── ex10_mm_protection.rb ├── ex10_mm_protection_demo.rb ├── ex10_mm_protection_test.rb ├── ex11_mm_virtual.rb ├── ex11_mm_virtual_demo.rb ├── ex11_mm_virtual_test.rb ├── ex12_drb_client_demo_norun.rb ├── ex12_drb_server_demo_norun.rb ├── ex13_identity_demo.rb ├── ex14_speed_demo.rb ├── ex1_account.rb ├── ex1_account_test.rb ├── ex2_proxy.rb ├── ex2_proxy_demo.rb ├── ex2_proxy_test.rb ├── ex3_protect.rb ├── ex3_protect_test.rb ├── ex4_soap_demo.rb ├── ex5_virtual.rb ├── ex5_virtual_test.rb ├── ex6_virtual_block.rb ├── ex6_virtual_block_demo.rb ├── ex6_virtual_block_test.rb ├── ex7_meth_missing_demo.rb ├── ex8_almost_demo.rb ├── ex9_meth_miss_proxy.rb ├── ex9_meth_miss_proxy_demo.rb └── ex9_meth_miss_proxy_test.rb ├── 11 ├── Rakefile ├── ex1_enhanced_writer.rb ├── ex1_enhanced_writer_demo.rb ├── ex1_enhanced_writer_test.rb ├── ex2_decorator.rb ├── ex2_demo.rb ├── ex2_misc_decorators.rb ├── ex2_simple_writer.rb ├── ex2_test.rb ├── ex3_forwardable.rb ├── ex3_forwardable_test.rb ├── ex4_wrap_demo.rb ├── ex5_module.rb ├── ex5_module_test.rb └── ex6_alias_method_chain_demo.rb ├── 12 ├── Rakefile ├── ex10_subvert_demo.rb ├── ex10a_subvert_demo.rb ├── ex11_thread_demo.rb ├── ex12_monitor_demo.rb ├── ex13_scattered_bad_demo.rb ├── ex14_scattered_good_demo.rb ├── ex15_subclass.rb ├── ex15_subclass_test.rb ├── ex1_class_attr_demo.rb ├── ex2_class_method_demo.rb ├── ex3_simple_logger.rb ├── ex3_simple_logger_demo.rb ├── ex3_simple_logger_test.rb ├── ex4_manual.rb ├── ex4_manual_demo.rb ├── ex4_manual_test.rb ├── ex5_private.rb ├── ex5_private_test.rb ├── ex6_std_singleton.rb ├── ex6_std_singleton_test.rb ├── ex7_global_demo.rb ├── ex8_class_singleton.rb ├── ex8_class_singleton_demo.rb ├── ex8_class_singleton_test.rb ├── ex9_module_singleton.rb ├── ex9_module_singleton_demo.rb ├── ex9_module_singleton_test.rb └── log.txt ├── 13 ├── Rakefile ├── ex10_habitat.rb ├── ex10_habitat_demo.rb ├── ex10_habitat_test.rb ├── ex11_abs_factories.rb ├── ex11_abs_factories_test.rb ├── ex12_abs_fac_habitat.rb ├── ex12_abs_fac_habitat_demo.rb ├── ex12_abs_fac_habitat_test.rb ├── ex13_abs_fac_with_classes.rb ├── ex13_abs_fac_with_classes_demo.rb ├── ex13_abs_fac_with_classes_test.rb ├── ex14_names.rb ├── ex14_names_demo.rb ├── ex14_names_test.rb ├── ex15_activerecord_demo.rb ├── ex1_duck.rb ├── ex1_duck_test.rb ├── ex2_pond.rb ├── ex2_pond_demo.rb ├── ex2_pond_test.rb ├── ex3_frog.rb ├── ex3_frog_test.rb ├── ex4_factory_method.rb ├── ex4_factory_method_demo.rb ├── ex4_factory_method_test.rb ├── ex5_plants.rb ├── ex5_plants_test.rb ├── ex6_extended_pond.rb ├── ex6_extended_pond_test.rb ├── ex7_param_pond.rb ├── ex7_param_pond_test.rb ├── ex8_factory_classes.rb ├── ex8_factory_classes_demo.rb ├── ex8_factory_classes_test.rb ├── ex9_tiger_tree.rb └── ex9_tiger_tree_test.rb ├── 14 ├── Rakefile ├── book.doc ├── ex1_computer.rb ├── ex1_computer_demo.rb ├── ex2_simple_builder.rb ├── ex2_simple_builder_demo.rb ├── ex2_simple_builder_test.rb ├── ex3_polymorphic.rb ├── ex3_polymorphic_test.rb ├── ex4_sane_builder.rb ├── ex4_sane_builder_test.rb ├── ex5_saner_builder.rb ├── ex5_saner_builder_test.rb ├── ex6_reuse_demo.rb ├── ex7_reset_builder.rb ├── ex7_reset_builder_test.rb ├── ex8_magic_method.rb ├── ex8_magic_method_demo.rb ├── ex8_magic_method_test.rb └── ex9_mail_demo.rb ├── 15 ├── Rakefile ├── dir1 │ ├── big.mp3 │ ├── small.mp3 │ ├── small1.txt │ └── small2.txt ├── dir2 │ ├── big.mp3 │ ├── small.mp3 │ ├── small1.txt │ ├── small2.txt │ └── subdir │ │ ├── other.mp3 │ │ └── small.jpg ├── ex1_files.rb ├── ex1_files_demo.rb ├── ex1_files_test.rb ├── ex2_parser.rb ├── ex2_parser_demo.rb ├── ex2_parser_test.rb ├── ex3_operators.rb ├── ex3_operators_demo.rb ├── ex3_operators_test.rb ├── ex4_speed_demo.rb ├── ex5_runt_demo.broken ├── music_dir │ ├── 1.mp3 │ ├── 2.mp3 │ └── 3.mp3 └── test_dir │ ├── big.mp3 │ ├── big2.mp3 │ ├── small.mp3 │ ├── small1.txt │ ├── small2.txt │ └── subdir │ ├── other.mp3 │ └── small.jpg ├── 16 ├── Rakefile ├── dir1 │ ├── a │ ├── b │ ├── poem.txt │ └── subdir │ │ ├── poem.txt │ │ ├── x │ │ └── y ├── first_example │ ├── backup.pr │ ├── data │ │ ├── documents │ │ │ └── speach.txt │ │ ├── images │ │ │ └── mona.jpeg │ │ └── music │ │ │ └── 9th.mp3 │ ├── ex2_pr_defs_demo.rb │ ├── ex3_packrat.rb │ ├── ex3_packrat_test.rb │ ├── ex4_real_defs.rb │ ├── ex4_real_defs_demo.rb │ └── finder.rb └── second_example │ ├── backup.pr │ ├── backup.rb │ ├── ex5_full_parser.rb │ ├── ex6_multi_backup.rb │ ├── ex6_multi_backup_demo.rb │ └── finder.rb ├── 17 ├── Rakefile ├── ex10_account_attr.rb ├── ex10_account_attr_test.rb ├── ex11_readable.rb ├── ex11_readable_test.rb ├── ex12_forwardable_demo.rb ├── ex13_active_record.rb ├── ex1_method.rb ├── ex1_method_demo.rb ├── ex1_method_test.rb ├── ex2_module.rb ├── ex2_module_test.rb ├── ex3_members.rb ├── ex4_base.rb ├── ex4_base_demo.rb ├── ex4_base_test.rb ├── ex5_member_of.rb ├── ex5_member_of_test.rb ├── ex6_responds.rb ├── ex6_responds_test.rb ├── ex7_lost_method.rb ├── ex8_lost_method_defense.rb ├── ex9_account_simple.rb └── ex9_account_simple_test.rb ├── 18 ├── Rakefile ├── adapters.yml ├── ex1_message.rb ├── ex1_message_test.rb ├── ex2_adapters.rb ├── ex3_hardcoded.rb ├── ex3_hardcoded_test.rb ├── ex4_byname.rb ├── ex4_byname_test.rb ├── ex5 │ ├── README.txt │ └── lib │ │ ├── adapter │ │ ├── file.rb │ │ ├── file_adapter.rb │ │ ├── file_adapter.rb~ │ │ ├── http_adapter.rb │ │ ├── http_adapter.rb~ │ │ ├── log.rb │ │ ├── smtp_adapter.rb │ │ └── smtp_adapter.rb~ │ │ ├── adapter_scaffold.rb │ │ ├── auth │ │ ├── default.rb │ │ ├── russolsen_dot_com.rb │ │ └── russolsen_dot_com.rb~ │ │ ├── gateway0.rb │ │ ├── gateway1.rb │ │ ├── message.rb │ │ ├── msg.out │ │ └── transform │ │ ├── from_russ_dot_olsen.erb │ │ └── to_russolsen_dot_com.erb ├── http_adapter.rb ├── http_server.rb └── msg ├── 19 ├── Rakefile ├── message.rb └── message2.rb ├── .gitignore ├── 01 ├── Rakefile ├── ex1_cars_demo.rb ├── ex2_vehicle_demo.rb ├── ex3_vehicle.rb ├── ex3_vehicle_test.rb ├── ex4_car.rb ├── ex4_car_test.rb ├── ex5_engine.rb ├── ex5_engine_test.rb ├── ex6_car_delegation.rb ├── ex6_car_delegation_test.rb ├── ex7_car_diesel.rb ├── ex7_car_diesel_test.rb ├── ex7_diesel.rb ├── ex8_delegate.rb └── ex8_delegate_test.rb ├── 02 ├── Rakefile ├── account.rb ├── ex10_array_demo.rb ├── ex11_hash_demo.rb ├── ex12_re_demo.rb ├── ex13_account.rb ├── ex13_account_demo.rb ├── ex13_account_test.rb ├── ex14_attribute_error_demo.rb ├── ex15_manual_accessor.rb ├── ex15_manual_accessor_demo.rb ├── ex15a_manual_accessor.rb ├── ex15a_manual_accessor_test.rb ├── ex16_accessors.rb ├── ex16_accessors_demo.rb ├── ex16_accessors_test.rb ├── ex17_self_demo.rb ├── ex18_interest_account.rb ├── ex18_interest_account_test.rb ├── ex19_interest_super.rb ├── ex19_interest_super_test.rb ├── ex1_hello_demo.rb ├── ex20_arg_demo.rb ├── ex21_module.rb ├── ex21_module_demo.rb ├── ex22_exception_demo.rb ├── ex23_require_demo.rb ├── ex2_variable_demo.rb ├── ex3_numbers_demo.rb ├── ex4_prims_demo.rb ├── ex5_nil_demo.rb ├── ex6_decisions_demo.rb ├── ex7_loop_demo.rb ├── ex8_string_demo.rb ├── ex9_symbols_demo.rb ├── ff └── xxx.rb ├── 03 ├── Empty.class ├── Empty.java ├── Rakefile ├── empty.rb ├── ex10_webbrick_demo_norun.rb ├── ex1_report.rb ├── ex1_report_demo.rb ├── ex2_hacked_report.rb ├── ex2_hacked_report_test.rb ├── ex3_template_report.rb ├── ex3_template_report_demo.rb ├── ex4_hook_report.rb ├── ex4_hook_report_test.rb ├── ex5_do_something_hook.rb ├── ex5_do_something_hook_test.rb ├── ex6_decls_demo.rb ├── ex7_empty.rb ├── ex7_empty_test.rb ├── ex8_using_demo.rb └── ex9_using_demo.rb ├── 04 ├── Rakefile ├── ex1_strategy.rb ├── ex1_strategy_demo.rb ├── ex1_strategy_test.rb ├── ex2_strategy_self.rb ├── ex2_strategy_self_test.rb ├── ex3_sans_superclass.rb ├── ex3_sans_superclass_test.rb ├── ex4_proc_demo.rb ├── ex5_strategy_proc.rb ├── ex5_strategy_proc_demo.rb ├── ex5_strategy_proc_test.rb └── ex6_sort_demo.rb ├── 05 ├── Rakefile ├── data.xml ├── ex10_consistent.rb ├── ex10_consistent_demo.rb ├── ex10_consistent_test.rb ├── ex11_activerecord.rb ├── ex12_xml_demo.rb ├── ex1_simple_emp.rb ├── ex1_simple_emp_demo.rb ├── ex2_hardcoded.rb ├── ex2_hardcoded_demo.rb ├── ex3_employee_obs.rb ├── ex3_employee_obs_demo.rb ├── ex4_subject_class.rb ├── ex4_subject_class_test.rb ├── ex4_subject_test_methods_module.rb ├── ex5_subject_module.rb ├── ex5_subject_module_test.rb ├── ex6_std_observer.rb ├── ex6_std_observer_test.rb ├── ex7_observer_proc.rb ├── ex7_observer_proc_demo.rb ├── ex7_observer_proc_test.rb ├── ex8_variations_demo.rb ├── ex9_chatter.rb └── ex9_chatter_test.rb ├── 06 ├── Rakefile ├── background.gif ├── ex10_ancestry_demo.rb ├── ex11_abuse_child.rb ├── ex11_abuse_child_test.rb ├── ex12_abuse_child.rb ├── ex12_abuse_child_test.rb ├── ex1_task.rb ├── ex1_task_test.rb ├── ex3_make_batter.rb ├── ex3_make_batter_test.rb ├── ex4_comp_task.rb ├── ex4_comp_task_test.rb ├── ex5_make_batter.rb ├── ex5_make_batter_test.rb ├── ex6_make_cake.rb ├── ex6_make_cake_test.rb ├── ex7_operators.rb ├── ex7_operators_demo.rb ├── ex7_operators_test.rb ├── ex8_array.rb ├── ex8_array_test.rb ├── ex9_parent.rb ├── ex9_parent_test.rb └── fxexample.rb ├── 07 ├── Rakefile ├── ex10_scan_demo.rb ├── ex11_various_demo.rb ├── ex1_array_iterator.rb ├── ex1_array_iterator_test.rb ├── ex2_array_iter_demo.rb ├── ex3_string_iter_demo.rb ├── ex4_for_each_demo.rb ├── ex5_merge.rb ├── ex5_merge_test.rb ├── ex6_enum_demo.rb ├── ex6a_enum_portfolio.rb ├── ex6a_enum_portfolio_demo.rb ├── ex6a_enum_portfolio_test.rb ├── ex7_change_resis_extern.rb ├── ex7_change_resis_extern_demo.rb ├── ex7_change_resis_extern_test.rb ├── ex8_changed_iternal_demo.rb ├── ex9_change_resis_for_each.rb ├── ex9_change_resis_for_each_test.rb ├── names.txt └── short.txt ├── 08 ├── 001_create_book_table.rb ├── 002_add_isbn_column.rb ├── Rakefile ├── ex1_slickbutton_demo.rb ├── ex2_button_with_cmd.rb ├── ex2_button_with_cmd_demo.rb ├── ex2_button_with_cmd_test.rb ├── ex3_block_button.rb ├── ex3_block_button_demo.rb ├── ex3_block_button_test.rb ├── ex4_file_cmd.rb ├── ex4_file_cmd_demo.rb ├── ex4_file_cmd_test.rb ├── ex5_undo.rb ├── ex5_undo_test.rb ├── ex6_overuse_demo.rb ├── ex7_madeline_demo.rb ├── existing_test_file.txt └── file2.txt ├── 09 ├── Rakefile ├── british_text_object.rb ├── cypher ├── ex1_encrypter.rb ├── ex1_encrypter_demo.rb ├── ex1_encrypter_test.rb ├── ex2_ioadapter.rb ├── ex2_ioadapter_demo.rb ├── ex2_ioadapter_test.rb ├── ex3_renderer.rb ├── ex3_renderer_test.rb ├── ex4_bto_adapter.rb ├── ex4_bto_adapter_test.rb ├── ex5_change_class.rb ├── ex5_change_class_test.rb ├── ex6_vandal_demo.rb ├── ex7_change_instance_demo.rb ├── ex7a_change_instance_demo.rb ├── message.encrypted ├── message.txt ├── new_message ├── out └── out.txt ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── example.rb ├── rake_utils.rb └── rvmrc.example /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | coverage 6 | InstalledFiles 7 | lib/bundler/man 8 | pkg 9 | rdoc 10 | spec/reports 11 | test/tmp 12 | test/version_tmp 13 | tmp 14 | *.swp 15 | .DS_Store 16 | 17 | # YARD artifacts 18 | .yardoc 19 | _yardoc 20 | doc/ 21 | 22 | # Test artifacts 23 | 08/employees 24 | -------------------------------------------------------------------------------- /01/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /01/ex1_cars_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | class Car 6 | def initialize 7 | puts "new car #{self}" 8 | end 9 | 10 | def drive(distance) 11 | puts "drive car #{self}: #{distance}" 12 | end 13 | end 14 | 15 | class AirPlane 16 | def initialize 17 | puts "new plane #{self}" 18 | end 19 | 20 | def fly(distance) 21 | puts "fly plane #{self}: #{distance}" 22 | end 23 | end 24 | 25 | 26 | example %q{ 27 | my_car = Car.new 28 | my_car.drive(200) 29 | 30 | } 31 | 32 | 33 | example %q{ 34 | is_car = true 35 | 36 | if is_car 37 | my_car = Car.new 38 | my_car.drive(200) 39 | else 40 | my_plane = AirPlane.new 41 | my_plane.fly(200) 42 | end 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /01/ex2_vehicle_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | class Car 6 | def initialize 7 | puts "new car #{self}" 8 | end 9 | 10 | def travel(distance) 11 | puts "drive car #{self}: #{distance}" 12 | end 13 | end 14 | 15 | class AirPlane 16 | def initialize 17 | puts "new plane #{self}" 18 | end 19 | 20 | def travel(distance) 21 | puts "fly plane #{self}: #{distance}" 22 | end 23 | end 24 | 25 | def get_vehicle 26 | if rand > 0.5 27 | AirPlane.new 28 | else 29 | Car.new 30 | end 31 | end 32 | 33 | example %q{ 34 | my_vehicle = get_vehicle 35 | my_vehicle.travel(200) 36 | } 37 | -------------------------------------------------------------------------------- /01/ex3_vehicle.rb: -------------------------------------------------------------------------------- 1 | class Vehicle 2 | 3 | # All sorts of vehicle related code... 4 | 5 | def start_engine 6 | # start the engine 7 | end 8 | 9 | def stop_engine 10 | # stop the engine 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /01/ex3_vehicle_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex3_vehicle' 7 | 8 | class VehicleTest < Test::Unit::TestCase 9 | 10 | 11 | def test_vehicle 12 | v = Vehicle.new 13 | v.start_engine 14 | v.stop_engine 15 | end 16 | 17 | end 18 | 19 | -------------------------------------------------------------------------------- /01/ex4_car.rb: -------------------------------------------------------------------------------- 1 | 2 | class Car < Vehicle 3 | def sunday_drive 4 | start_engine 5 | # cruise out into the country and return 6 | stop_engine 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /01/ex4_car_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex3_vehicle' 7 | require_relative 'ex4_car' 8 | 9 | class CarTest < Test::Unit::TestCase 10 | 11 | def test_cat 12 | c = Car.new 13 | c.start_engine 14 | c.stop_engine 15 | c.sunday_drive 16 | end 17 | 18 | end 19 | 20 | -------------------------------------------------------------------------------- /01/ex5_engine.rb: -------------------------------------------------------------------------------- 1 | class Engine 2 | 3 | # All sorts of engine related code... 4 | 5 | def start 6 | # start the engine 7 | end 8 | 9 | def stop 10 | # stop the engine 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /01/ex5_engine_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex5_engine' 7 | 8 | class EngineTest < Test::Unit::TestCase 9 | 10 | 11 | def test_engine 12 | e = Engine.new 13 | e.start 14 | e.stop 15 | end 16 | 17 | end 18 | 19 | -------------------------------------------------------------------------------- /01/ex6_car_delegation.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Car 4 | def initialize 5 | @engine = Engine.new 6 | end 7 | 8 | def sunday_drive 9 | @engine.start 10 | # cruise out into the country and return ... 11 | @engine.stop 12 | end 13 | end 14 | 15 | -------------------------------------------------------------------------------- /01/ex6_car_delegation_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex5_engine' 7 | require_relative 'ex6_car_delegation' 8 | 9 | class CarWithDelegationTest < Test::Unit::TestCase 10 | 11 | def test_car 12 | c = Car.new 13 | c.sunday_drive 14 | end 15 | 16 | end 17 | 18 | -------------------------------------------------------------------------------- /01/ex7_car_diesel.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Car 4 | def initialize 5 | @engine = GasolineEngine.new 6 | end 7 | 8 | def sunday_drive 9 | @engine.start 10 | # cruise out into the country and return ... 11 | @engine.stop 12 | end 13 | 14 | def switch_to_diesel 15 | @engine = DieselEngine.new 16 | end 17 | end 18 | 19 | -------------------------------------------------------------------------------- /01/ex7_car_diesel_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex7_car_diesel' 7 | require_relative 'ex7_diesel' 8 | 9 | class SwitchEngineTest < Test::Unit::TestCase 10 | 11 | def test_switching_engine 12 | c = Car.new 13 | assert_equal GasolineEngine, c.instance_variable_get(:@engine).class 14 | c.sunday_drive 15 | c.switch_to_diesel 16 | assert_equal DieselEngine, c.instance_variable_get(:@engine).class 17 | c.sunday_drive 18 | end 19 | 20 | end 21 | 22 | -------------------------------------------------------------------------------- /01/ex7_diesel.rb: -------------------------------------------------------------------------------- 1 | 2 | class GasolineEngine 3 | def start 4 | puts 'starting a gas engine' 5 | end 6 | 7 | def stop 8 | puts 'stopping a gas engine' 9 | end 10 | end 11 | 12 | class DieselEngine 13 | def start 14 | puts 'starting a diesel engine' 15 | end 16 | 17 | def stop 18 | puts 'stopping a diesel engine' 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /01/ex8_delegate.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Car 4 | def initialize 5 | @engine = GasolineEngine.new 6 | end 7 | 8 | def sunday_drive 9 | @engine.start 10 | # cruise out into the country and return ... 11 | @engine.stop 12 | end 13 | 14 | def switch_to_diesel 15 | @engine = DieselEngine.new 16 | end 17 | 18 | def start_engine 19 | @engine.start 20 | end 21 | 22 | def stop_engine 23 | @engine.stop 24 | end 25 | end 26 | 27 | -------------------------------------------------------------------------------- /01/ex8_delegate_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex8_delegate' 7 | require_relative 'ex7_diesel' 8 | 9 | class DelegateToEngineTest < Test::Unit::TestCase 10 | 11 | def test_delegation 12 | c = Car.new 13 | assert_equal GasolineEngine, c.instance_variable_get(:@engine).class 14 | c.sunday_drive 15 | c.switch_to_diesel 16 | assert_equal DieselEngine, c.instance_variable_get(:@engine).class 17 | c.sunday_drive 18 | 19 | c.start_engine 20 | c.stop_engine 21 | end 22 | 23 | end 24 | 25 | -------------------------------------------------------------------------------- /02/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /02/account.rb: -------------------------------------------------------------------------------- 1 | require_relative 'ex13_account' 2 | require_relative 'ex16_accessors' 3 | 4 | -------------------------------------------------------------------------------- /02/ex11_hash_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example %q{ 5 | h = {} 6 | h['first_name'] = 'Albert' 7 | h['last_name'] = 'Einstein' 8 | 9 | h['first_name'] # is 'Albert' 10 | h['last_name'] # is Einstein 11 | 12 | 13 | 14 | puts h['first_name'] 15 | puts h['last_name'] 16 | } 17 | 18 | example %q{ 19 | h = {'first_name' => 'Albert', 'last_name' => 'Einstein'} 20 | } 21 | 22 | example %q{ 23 | h = { :first_name => 'Albert', :last_name => 'Einstein' } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /02/ex12_re_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example %q{ 5 | /old/ 6 | /Russ|Russell/ 7 | /.*/ 8 | } 9 | 10 | example_expected 5, %q{/old/ =~ 'this old house' # 5 - the index of 'old'} 11 | example_expected nil, %q{ /Russ|Russell/ =~ 'Fred' # nil - Russ is not Fred} 12 | example_expected 0, %q{/.*/ =~ 'any old string' # 0 - the RE will match anything} 13 | 14 | -------------------------------------------------------------------------------- /02/ex13_account.rb: -------------------------------------------------------------------------------- 1 | 2 | class BankAccount 3 | def initialize( account_owner ) 4 | @owner = account_owner 5 | @balance = 0 6 | end 7 | 8 | def deposit( amount ) 9 | @balance = @balance + amount 10 | end 11 | 12 | def withdraw( amount ) 13 | @balance = @balance - amount 14 | end 15 | end 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /02/ex13_account_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require_relative 'ex13_account' 5 | 6 | example %q{ 7 | my_account = BankAccount.new('Russ') 8 | 9 | my_account = BankAccount.new 'Russ' 10 | } 11 | -------------------------------------------------------------------------------- /02/ex13_account_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex13_account' 7 | 8 | class AccountTest < Test::Unit::TestCase 9 | def test_account 10 | ba = BankAccount.new('Russ') 11 | assert_equal 0, ba.instance_variable_get(:@balance) 12 | 13 | assert_equal 200, ba.deposit(200) 14 | assert_equal 200, ba.instance_variable_get(:@balance) 15 | 16 | assert_equal 180, ba.withdraw(20) 17 | assert_equal 180, ba.instance_variable_get(:@balance) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /02/ex14_attribute_error_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | 4 | require_relative 'ex13_account' 5 | 6 | 7 | my_account = BankAccount.new('russ') 8 | 9 | begin 10 | puts(my_account.balance) 11 | rescue 12 | puts "Got an error" 13 | end 14 | -------------------------------------------------------------------------------- /02/ex15_manual_accessor.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class BankAccount 4 | def balance 5 | return @balance 6 | end 7 | 8 | def set_balance(new_balance) 9 | @balance = new_balance 10 | end 11 | 12 | def balance=(new_balance) 13 | @balance = new_balance 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /02/ex15_manual_accessor_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require_relative 'ex13_account' 5 | require_relative 'ex15_manual_accessor' 6 | 7 | 8 | example %q{ 9 | my_account = BankAccount.new('russ') 10 | 11 | puts(my_account.balance) 12 | } 13 | 14 | 15 | 16 | example %q{ 17 | my_account = BankAccount.new('russ') 18 | 19 | 20 | 21 | my_account.set_balance(100) 22 | 23 | puts(my_account.balance) 24 | } 25 | 26 | example %q{ 27 | my_account = BankAccount.new('russ') 28 | 29 | my_account.balance = 100 30 | 31 | puts(my_account.balance) 32 | } 33 | -------------------------------------------------------------------------------- /02/ex15a_manual_accessor.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class BankAccount 4 | 5 | def balance 6 | @balance 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /02/ex15a_manual_accessor_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex13_account' 7 | require_relative 'ex15_manual_accessor' 8 | require_relative 'ex15a_manual_accessor' 9 | 10 | class AccountTest < Test::Unit::TestCase 11 | 12 | def test_account 13 | ba = BankAccount.new('Russ') 14 | assert_equal 0, ba.balance 15 | 16 | assert_equal 200, ba.deposit(200) 17 | assert_equal 200, ba.balance 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /02/ex16_accessors.rb: -------------------------------------------------------------------------------- 1 | class BankAccount 2 | attr_accessor :balance 3 | attr_reader :owner 4 | end 5 | -------------------------------------------------------------------------------- /02/ex16_accessors_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | class Example 8 | attr_accessor :balance, :grace, :agility 9 | end 10 | 11 | e = Example.new 12 | e.balance=:good 13 | e.grace=:ok 14 | e.agility=:bad 15 | 16 | pp e 17 | } 18 | 19 | 20 | example %q{ 21 | 22 | class Example 23 | attr_reader :name 24 | 25 | def initialize 26 | @name = 'russ' 27 | end 28 | end 29 | 30 | e = Example.new 31 | puts e.name 32 | 33 | } 34 | 35 | example %q{ 36 | 37 | class Example 38 | attr_writer :name 39 | 40 | def initialize 41 | @name = 'russ' 42 | end 43 | end 44 | 45 | e = Example.new 46 | puts e.instance_variable_get(:@name) 47 | 48 | e.name = 'Fred' 49 | puts e.instance_variable_get(:@name) 50 | 51 | } 52 | -------------------------------------------------------------------------------- /02/ex16_accessors_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex13_account' 7 | require_relative 'ex16_accessors' 8 | 9 | class AccountAccessorsTest < Test::Unit::TestCase 10 | 11 | def test_account 12 | ba = BankAccount.new('Russ') 13 | assert_equal 0, ba.balance 14 | 15 | assert_equal 200, ba.deposit(200) 16 | assert_equal 200, ba.balance 17 | 18 | assert_equal 180, ba.withdraw(20) 19 | assert_equal 180, ba.balance 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /02/ex17_self_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require '../example' 3 | 4 | require_relative 'ex13_account' 5 | require_relative 'ex15_manual_accessor' 6 | 7 | 8 | example %q{ 9 | class SelfCentered 10 | def talk_about_me 11 | puts "Hello I am #{self}" 12 | end 13 | end 14 | 15 | conceited = SelfCentered.new 16 | conceited.talk_about_me 17 | } 18 | -------------------------------------------------------------------------------- /02/ex18_interest_account.rb: -------------------------------------------------------------------------------- 1 | require_relative 'account' 2 | 3 | class InterestBearingAccount < BankAccount 4 | def initialize(owner, rate) 5 | @owner = owner 6 | @balance = 0 7 | @rate = rate 8 | end 9 | 10 | def deposit_interest 11 | @balance += @rate * @balance 12 | end 13 | end 14 | 15 | 16 | -------------------------------------------------------------------------------- /02/ex18_interest_account_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex13_account' 7 | require_relative 'ex16_accessors' 8 | require_relative 'ex18_interest_account' 9 | 10 | class InterestBearingAccountTest < Test::Unit::TestCase 11 | def test_account 12 | ba = InterestBearingAccount.new('Russ', 0.1) 13 | assert_equal 'Russ', ba.owner 14 | assert_equal 0, ba.balance 15 | 16 | assert_equal 100, ba.deposit(100) 17 | assert_equal 100, ba.balance 18 | 19 | ba.deposit_interest 20 | 21 | assert_equal 110, ba.balance 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /02/ex19_interest_super.rb: -------------------------------------------------------------------------------- 1 | require_relative 'account' 2 | 3 | 4 | class InterestBearingAccount < BankAccount 5 | def initialize(owner, rate) 6 | super(owner) 7 | @rate = rate 8 | end 9 | 10 | def deposit_interest 11 | @balance += @rate * @balance 12 | end 13 | end 14 | 15 | 16 | -------------------------------------------------------------------------------- /02/ex19_interest_super_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex13_account' 7 | require_relative 'ex16_accessors' 8 | require_relative 'ex18_interest_account' 9 | 10 | class InterestBearingAccountWithSuperTest < Test::Unit::TestCase 11 | 12 | def test_account 13 | ba = InterestBearingAccount.new('Russ', 0.1) 14 | assert_equal 'Russ', ba.owner 15 | assert_equal 0, ba.balance 16 | 17 | assert_equal 100, ba.deposit(100) 18 | assert_equal 100, ba.balance 19 | 20 | ba.deposit_interest 21 | 22 | assert_equal 110, ba.balance 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /02/ex1_hello_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example %q{ 5 | # 6 | # The traditional first program in any language. 7 | # 8 | puts( 'hello world' ) 9 | } 10 | 11 | example %q{ 12 | puts( 'hello world' ) # say hello 13 | } 14 | 15 | example %q{ 16 | # 17 | # A legal, but very untypical Ruby use of the semicolon. 18 | # 19 | puts('hello world'); 20 | # 21 | # A little more de rigueur, but still rare use of semicolons 22 | # 23 | puts('hello '); puts('world') 24 | 25 | } 26 | 27 | example %q{ 28 | x = 10 + 29 | 20 + 30 30 | } 31 | 32 | example %q{ 33 | x = 10 \ 34 | + 10 35 | } 36 | 37 | example %q{ 38 | puts 'hello world' 39 | } 40 | 41 | example %q{ 42 | puts "hello world" 43 | puts 'A backslash: \ a single quote \' the end' 44 | } 45 | -------------------------------------------------------------------------------- /02/ex20_arg_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example %q{ 5 | 6 | def create_car( model, convertible=false) 7 | # ... 8 | 9 | puts "model: #{model} convertible: #{convertible}" 10 | 11 | end 12 | 13 | create_car('sedan') 14 | create_car('sports car', true) 15 | create_car('minivan', false) 16 | } 17 | 18 | 19 | example %q{ 20 | def add_students(*names) 21 | for student in names 22 | puts "adding student #{student}" 23 | end 24 | end 25 | 26 | add_students( "Fred Smith", "Bob Tanner" ) 27 | } 28 | 29 | 30 | example %q{ 31 | def describe_hero(name, *super_powers) 32 | puts "Name: #{name}" 33 | for power in super_powers 34 | puts "Super power: #{power}" 35 | end 36 | end 37 | 38 | describe_hero( "Batman" ) 39 | describe_hero( "Flash", "speed" ) 40 | describe_hero( "Superman", "can fly", "x-ray vision", "invulnerable" ) 41 | } 42 | -------------------------------------------------------------------------------- /02/ex21_module.rb: -------------------------------------------------------------------------------- 1 | 2 | module HelloModule 3 | def say_hello 4 | puts 'Hello out there.' 5 | end 6 | end 7 | 8 | class TryIt 9 | include HelloModule 10 | end 11 | 12 | 13 | module Chatty 14 | def say_hi 15 | puts("Hello, my name is #{name}") 16 | puts("My job title is #{title}") 17 | puts("I work in the #{department} department") 18 | end 19 | end 20 | 21 | class Employee 22 | include Chatty 23 | 24 | def name 25 | 'Fred' 26 | end 27 | 28 | def title 29 | 'Janitor' 30 | end 31 | 32 | def department 33 | 'Maintenance' 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /02/ex21_module_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | 5 | require_relative 'ex21_module' 6 | 7 | example %q{ 8 | 9 | tryit = TryIt.new 10 | tryit.say_hello 11 | 12 | } 13 | 14 | 15 | example %q{ 16 | 17 | fred = Employee.new 18 | fred.say_hi 19 | 20 | } 21 | -------------------------------------------------------------------------------- /02/ex22_exception_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | 5 | example %q{ 6 | begin 7 | quotient = 1 / 0 # Boom! 8 | rescue 9 | puts 'Something bad happened' 10 | end 11 | } 12 | 13 | example %q{ 14 | begin 15 | quotient = 1 / 0 # Boom! 16 | rescue ZeroDivisionError 17 | puts 'You tried to divide by zero' 18 | end 19 | } 20 | 21 | expect_error %q{ 22 | denominator = 0 23 | 24 | 25 | if denominator == 0 26 | raise ZeroDivisionError 27 | end 28 | return numerator / denominator 29 | } 30 | 31 | # 32 | # shortcuts 33 | # 34 | 35 | expect_error %q{ raise ZeroDivisionError.new } 36 | expect_error %q{ raise ZeroDivisionError } 37 | expect_error %q{ raise 'Something bad happened' } 38 | 39 | -------------------------------------------------------------------------------- /02/ex23_require_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example(%q{ 5 | require_relative 'account.rb' 6 | 7 | class Portfolio 8 | # Uses BankAccount. 9 | 10 | 11 | ba = BankAccount.new('russ') 12 | end 13 | }, __FILE__) 14 | 15 | 16 | example(%q{ 17 | require_relative 'account' 18 | }, __FILE__) 19 | 20 | 21 | example %q{ 22 | 23 | require 'uri' 24 | yahoo = URI.parse('http://www.yahoo.com') 25 | 26 | } 27 | 28 | 29 | example %q{ 30 | 31 | require 'rubygems' 32 | require 'pry' 33 | 34 | } 35 | -------------------------------------------------------------------------------- /02/ex2_variable_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example %q{ 5 | 6 | max_length = 44 7 | maxLength = 44 8 | numberPages = 44 9 | numberpages = 44 10 | a_very_long_variable_name = 44 11 | _flag = 44 12 | column77Row88 = 44 13 | ___ = 44 14 | 15 | puts max_length 16 | puts maxLength 17 | puts numberPages 18 | puts numberpages 19 | puts a_very_long_variable_name 20 | puts _flag 21 | puts column77Row88 22 | 23 | max_length 24 | maxLength 25 | numberPages 26 | numberpages 27 | a_very_long_variable_name 28 | _flag 29 | column77Row88 30 | ___ 31 | 32 | } 33 | 34 | 35 | example %q{ 36 | first_name = 'russ' 37 | last_name = 'olsen' 38 | full_name = first_name + ' ' + last_name 39 | 40 | puts full_name 41 | } 42 | 43 | example %q{ 44 | POUNDS_PER_KILOGRAM = 2.2 45 | StopToken = 'end' 46 | FACTS = 'Death and taxes' 47 | 48 | puts POUNDS_PER_KILOGRAM 49 | puts StopToken 50 | puts FACTS 51 | } 52 | 53 | example %q{ 54 | StopToken = 'end' 55 | } 56 | 57 | -------------------------------------------------------------------------------- /02/ex4_prims_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example_expected Fixnum, %q{ 7.class # Gives you the class Fixnum } 5 | example_expected Bignum, %q{ 88888888888888888888.class # Gives you the class Bignum } 6 | example_expected Float, %q{ 3.14159.class # Gives you the class Float } 7 | 8 | example_expected 4, %q{3.7.round # gives us 4.0} 9 | example_expected 3, %q{3.7.truncate # gives us 3} 10 | example_expected 123, %q{-123.abs # absolute value, 123} 11 | example_expected 2, %q{1.succ # successor, or next number, 2} 12 | 13 | example_expected true, %q{'hello'.instance_of?(String) # true} 14 | example_expected false, %q{'hello'.nil? # false} 15 | 16 | example_expected '44', %q{44.to_s # returns a two character string '44'} 17 | example_expected 'hello', %q{'hello'.to_s # A fairly boring conversion, returns 'hello'} 18 | -------------------------------------------------------------------------------- /02/ex7_loop_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example %q{ 5 | i = 0 6 | while i < 4 7 | puts "i = #{i}" 8 | i = i + 1 9 | end 10 | } 11 | 12 | example %q{ 13 | i = 0 14 | until i >= 4 15 | puts "i = #{i}" 16 | i = i + 1 17 | end 18 | } 19 | 20 | example %q{ 21 | array = ['first', 'second', 'third'] 22 | 23 | for element in array 24 | puts element 25 | end 26 | } 27 | 28 | example %q{ 29 | array = ['first', 'second', 'third'] 30 | 31 | array.each do |x| 32 | puts x 33 | end 34 | } 35 | 36 | example %q{ 37 | names = ['george', 'mike', 'gary', 'diana'] 38 | 39 | names.each do |name| 40 | if name == 'gary' 41 | puts 'Break!' 42 | break 43 | end 44 | puts name 45 | end 46 | } 47 | 48 | 49 | example %q{ 50 | names = ['george', 'mike', 'gary', 'diana'] 51 | 52 | 53 | names.each do |name| 54 | if name == 'gary' 55 | puts 'Next!' 56 | next 57 | end 58 | puts name 59 | end 60 | } 61 | -------------------------------------------------------------------------------- /02/ex9_symbols_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example %q{ 5 | :a_symbol 6 | :an_other_symbol 7 | :first_name 8 | } 9 | -------------------------------------------------------------------------------- /02/ff: -------------------------------------------------------------------------------- 1 | example_expected nil, %q{/Russ|Russell/ =~ 'Fred' # nil – Fred is not Russ nor Russell} 2 | 3 | -------------------------------------------------------------------------------- /02/xxx.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | 2 # a Fixnum 5 | 437 # a Fixnum 6 | 2**437 # very definitely a big, Bignum 7 | 123456789000000000000 # Another Bignum 8 | 1234567890/1234567890 # Divide 2 Bignums, and get 1, a Fixnum 9 | 10 | raise 'error' unless (2).class == Fixnum 11 | raise 'error' unless (437).class == Fixnum 12 | raise 'error' unless (2**437).class == Bignum 13 | raise 'error' unless (123456789000000000000).class == Bignum 14 | raise 'error' unless (1234567890/1234567890).class == Fixnum 15 | -------------------------------------------------------------------------------- /03/Empty.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/russolsen/design_patterns_in_ruby_code/f62eaf1cd9d53d68ba6a87e0f9b81d635ee89ec2/03/Empty.class -------------------------------------------------------------------------------- /03/Empty.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | public class Empty 4 | { 5 | 6 | public boolean isEmpty(String s) 7 | { 8 | return s.length() == 0; 9 | } 10 | 11 | public void doit(String s) 12 | { 13 | System.out.println( "For string [" + s + "] empty = " + isEmpty(s) ); 14 | } 15 | 16 | public static void main(String[] args) 17 | { 18 | Empty empty = new Empty(); 19 | 20 | empty.doit("foo"); 21 | empty.doit("bar"); 22 | empty.doit(""); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /03/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /03/empty.rb: -------------------------------------------------------------------------------- 1 | require_relative 'ex7_empty' 2 | -------------------------------------------------------------------------------- /03/ex10_webbrick_demo_norun.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | require 'webrick' 8 | 9 | class HelloServer < WEBrick::GenericServer 10 | def run(socket) 11 | socket.print('Hello TCP/IP world') 12 | end 13 | end 14 | 15 | 16 | 17 | 18 | 19 | s = HelloServer.new(:Port => 2000) 20 | trap("INT"){ s.shutdown} 21 | s.start 22 | 23 | } 24 | -------------------------------------------------------------------------------- /03/ex1_report.rb: -------------------------------------------------------------------------------- 1 | 2 | class Report 3 | def initialize 4 | @title = 'Monthly Report' 5 | @text = ['Things are going', 'really, really well.'] 6 | end 7 | 8 | def output_report 9 | puts('') 10 | puts(' ') 11 | puts(" #{@title}") 12 | puts(' ') 13 | puts(' ') 14 | @text.each do |line| 15 | puts("

#{line}

" ) 16 | end 17 | puts(' ') 18 | puts('') 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /03/ex1_report_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | 6 | require_relative 'ex1_report' 7 | 8 | example %q{ 9 | 10 | report = Report.new 11 | report.output_report 12 | 13 | } 14 | -------------------------------------------------------------------------------- /03/ex2_hacked_report.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Report 4 | def initialize 5 | @title = 'Monthly Report' 6 | @text = ['Things are going', 'really, really well.'] 7 | end 8 | 9 | def output_report(format) 10 | if format == :plain 11 | puts("*** #{@title} ***") 12 | elsif format == :html 13 | puts('') 14 | puts(' ') 15 | puts(" #{@title}") 16 | puts(' ') 17 | puts(' ') 18 | else 19 | raise "Unknown format: #{format}" 20 | end 21 | 22 | @text.each do |line| 23 | if format == :plain 24 | puts(line) 25 | else 26 | puts("

#{line}

" ) 27 | end 28 | end 29 | 30 | if format == :html 31 | puts(' ') 32 | puts('') 33 | end 34 | end 35 | end 36 | 37 | -------------------------------------------------------------------------------- /03/ex2_hacked_report_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | require 'stringio' 6 | 7 | require_relative 'ex2_hacked_report' 8 | 9 | 10 | class HackedTest < Test::Unit::TestCase 11 | 12 | def test_hacked_report 13 | r = Report.new 14 | output = capture_output { r.output_report(:plain) } 15 | puts output 16 | assert /Things/ =~ output 17 | 18 | output = capture_output { r.output_report(:html) } 19 | puts output 20 | assert // =~ output 21 | assert /Monthly/ =~ output 22 | assert_raise(RuntimeError){ r.output_report(:pdf) } 23 | 24 | end 25 | 26 | def capture_output(&block) 27 | output = StringIO.new 28 | begin 29 | $stdout = output 30 | block.call 31 | ensure 32 | $stdout = STDOUT 33 | end 34 | output.string 35 | end 36 | 37 | end 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /03/ex3_template_report_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex3_template_report' 5 | 6 | example %q{ 7 | report = HTMLReport.new 8 | report.output_report 9 | 10 | report = PlainTextReport.new 11 | report.output_report 12 | 13 | } 14 | -------------------------------------------------------------------------------- /03/ex4_hook_report_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | require 'stringio' 6 | 7 | require_relative 'ex4_hook_report' 8 | 9 | 10 | class HookReportTest < Test::Unit::TestCase 11 | 12 | def test_hacked_report 13 | r = PlainTextReport.new 14 | output = capture_output { r.output_report } 15 | assert /Things/ =~ output 16 | 17 | r = HTMLReport.new 18 | output = capture_output { r.output_report } 19 | assert /<html>/ =~ output 20 | assert /<title>Monthly/ =~ output 21 | end 22 | 23 | def capture_output(&block) 24 | output = StringIO.new 25 | begin 26 | $stdout = output 27 | block.call 28 | ensure 29 | $stdout = STDOUT 30 | end 31 | output.string 32 | end 33 | 34 | end 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /03/ex5_do_something_hook.rb: -------------------------------------------------------------------------------- 1 | class Report 2 | def initialize 3 | @title = 'Monthly Report' 4 | @text = ['Things are going', 'really, really well.'] 5 | end 6 | 7 | def output_report 8 | output_start 9 | output_head 10 | output_body_start 11 | @text.each do |line| 12 | output_line(line) 13 | end 14 | output_body_end 15 | output_end 16 | end 17 | 18 | def output_start 19 | end 20 | 21 | def output_head 22 | output_line(@title) 23 | end 24 | 25 | def output_body_start 26 | end 27 | 28 | def output_line(line) 29 | raise 'Called abstract method: output_line' 30 | end 31 | 32 | def output_body_end 33 | end 34 | 35 | def output_end 36 | end 37 | end 38 | 39 | 40 | 41 | class PlainTextReport < Report 42 | def output_line(line) 43 | puts(line) 44 | end 45 | end 46 | 47 | 48 | -------------------------------------------------------------------------------- /03/ex5_do_something_hook_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | require 'stringio' 6 | 7 | require_relative 'ex5_do_something_hook' 8 | 9 | 10 | class DosomethingHookReportTest < Test::Unit::TestCase 11 | 12 | def test_hacked_report 13 | r = PlainTextReport.new 14 | output = capture_output { r.output_report } 15 | assert /Things/ =~ output 16 | end 17 | 18 | def capture_output(&block) 19 | output = StringIO.new 20 | begin 21 | $stdout = output 22 | block.call 23 | ensure 24 | $stdout = STDOUT 25 | end 26 | output.string 27 | end 28 | 29 | end 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /03/ex6_decls_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex3_template_report' 5 | 6 | example %q{ 7 | report = HTMLReport.new 8 | } 9 | 10 | -------------------------------------------------------------------------------- /03/ex7_empty.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | def empty?(s) 4 | s.length == 0 5 | end 6 | -------------------------------------------------------------------------------- /03/ex7_empty_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require_relative 'empty' 5 | 6 | class EmptyTest < Test::Unit::TestCase 7 | def setup 8 | @empty_string = '' 9 | @one_char_string = 'X' 10 | @long_string = 'The rain in Spain' 11 | 12 | @empty_array = [] 13 | @one_element_array = [1] 14 | @long_array = [1, 2, 3, 4, 5, 6] 15 | end 16 | 17 | def test_empty_on_strings 18 | assert empty?(@empty_string) 19 | assert ! empty?(@one_char_string) 20 | assert ! empty?(@long_string) 21 | end 22 | 23 | def test_empty_on_arrays 24 | assert empty?(@empty_array) 25 | assert ! empty?(@one_element_array) 26 | assert ! empty?(@long_array) 27 | end 28 | end 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /03/ex8_using_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | class Report 8 | def initialize 9 | @title = 'MonthlyReport' 10 | @text = ['Things are going', 'really, really well.'] 11 | end 12 | 13 | def output_report 14 | puts('<html>') 15 | puts(' <head>') 16 | puts(' <title>#{@title}') 17 | # output the rest of the report ... 18 | end 19 | end 20 | 21 | 22 | r = Report.new 23 | r.output_report 24 | 25 | } 26 | -------------------------------------------------------------------------------- /03/ex9_using_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | class Report 8 | def initialize 9 | @title = 'MonthlyReport' 10 | @text = [ 'Things are going', 'really, really well.' ] 11 | end 12 | 13 | 14 | 15 | 16 | # ... 17 | 18 | def output_report 19 | output_start 20 | output_title(@title) 21 | output_body_start 22 | for line in @text 23 | output_line(line) 24 | end 25 | output_body_end 26 | output_end 27 | end 28 | 29 | def output_start 30 | puts("") 31 | end 32 | 33 | def output_title(title) 34 | puts(' ') 35 | puts(" #{title}") 36 | puts(' ') 37 | end 38 | 39 | # ... 40 | 41 | 42 | 43 | 44 | 45 | def output_line(line) 46 | puts("

#{line}

") 47 | end 48 | 49 | def output_body_start 50 | end 51 | 52 | def output_body_end 53 | end 54 | 55 | def output_end 56 | end 57 | end 58 | 59 | 60 | r = Report.new 61 | r.output_report 62 | } 63 | -------------------------------------------------------------------------------- /04/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /04/ex1_strategy.rb: -------------------------------------------------------------------------------- 1 | class Formatter 2 | def output_report(title, text) 3 | raise 'Abstract method called' 4 | end 5 | end 6 | 7 | class HTMLFormatter < Formatter 8 | def output_report( title, text ) 9 | puts('') 10 | puts(' ') 11 | puts(" #{title}") 12 | puts(' ') 13 | puts(' ') 14 | text.each do |line| 15 | puts("

#{line}

" ) 16 | end 17 | puts(' ') 18 | puts('') 19 | end 20 | end 21 | 22 | class PlainTextFormatter < Formatter 23 | def output_report(title, text) 24 | puts("***** #{title} *****") 25 | text.each do |line| 26 | puts(line) 27 | end 28 | end 29 | end 30 | 31 | class Report 32 | attr_reader :title, :text 33 | attr_accessor :formatter 34 | 35 | def initialize(formatter) 36 | @title = 'Monthly Report' 37 | @text = ['Things are going', 'really, really well.'] 38 | @formatter = formatter 39 | end 40 | 41 | def output_report 42 | @formatter.output_report( @title, @text) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /04/ex1_strategy_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex1_strategy' 5 | 6 | example %q{ 7 | 8 | report = Report.new(HTMLFormatter.new) 9 | report.output_report 10 | 11 | } 12 | 13 | 14 | example %q{ 15 | 16 | report = Report.new(HTMLFormatter.new) 17 | report.output_report 18 | 19 | report.formatter = PlainTextFormatter.new 20 | report.output_report 21 | 22 | } 23 | -------------------------------------------------------------------------------- /04/ex2_strategy_self.rb: -------------------------------------------------------------------------------- 1 | 2 | class Report 3 | attr_reader :title, :text 4 | 5 | attr_accessor :formatter 6 | 7 | def initialize(formatter) 8 | @title = 'Monthly Report' 9 | @text = ['Things are going', 'really, really well.'] 10 | @formatter = formatter 11 | end 12 | 13 | def output_report 14 | @formatter.output_report(self) 15 | end 16 | end 17 | 18 | 19 | class Formatter 20 | def output_report(context) 21 | raise 'Abstract method called' 22 | end 23 | end 24 | 25 | class HTMLFormatter < Formatter 26 | def output_report(context) 27 | puts('') 28 | puts(' ') 29 | puts(" #{context.title}") 30 | puts(' ') 31 | puts(' ') 32 | context.text.each do |line| 33 | puts("

#{line}

") 34 | end 35 | puts(' ') 36 | puts('') 37 | end 38 | end 39 | 40 | class PlainTextFormatter < Formatter 41 | def output_report(context) 42 | puts("***** #{context.title} *****") 43 | context.text.each do |line| 44 | puts(line) 45 | end 46 | end 47 | end 48 | 49 | -------------------------------------------------------------------------------- /04/ex2_strategy_self_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'stringio' 5 | 6 | require_relative 'ex2_strategy_self' 7 | 8 | 9 | class FormatterTest < Test::Unit::TestCase 10 | 11 | def test_html_report 12 | r = Report.new(HTMLFormatter.new) 13 | 14 | output = capture_output { r.output_report } 15 | assert // =~ output 16 | assert /Monthly/ =~ output 17 | assert /<p>Things/ =~ output 18 | end 19 | 20 | def test_plain_report 21 | r = Report.new(PlainTextFormatter.new) 22 | 23 | output = capture_output { r.output_report } 24 | assert /Monthly/ =~ output 25 | assert /Things/ =~ output 26 | end 27 | 28 | def capture_output(&block) 29 | output = StringIO.new 30 | begin 31 | $stdout = output 32 | block.call 33 | ensure 34 | $stdout = STDOUT 35 | end 36 | output.string 37 | end 38 | 39 | end 40 | 41 | 42 | -------------------------------------------------------------------------------- /04/ex3_sans_superclass.rb: -------------------------------------------------------------------------------- 1 | 2 | class Report 3 | attr_reader :title, :text 4 | attr_accessor :formatter 5 | 6 | def initialize(formatter) 7 | @title = 'Monthly Report' 8 | @text = ['Things are going', 'really, really well.'] 9 | @formatter = formatter 10 | end 11 | 12 | def output_report 13 | @formatter.output_report(self) 14 | end 15 | end 16 | 17 | class HTMLFormatter 18 | def output_report(context) 19 | puts('<html>') 20 | puts(' <head>') 21 | # Output The rest of the report ... 22 | 23 | puts(" <title>#{context.title}") 24 | puts(' ') 25 | puts(' ') 26 | context.text.each do |line| 27 | puts("

#{line}

") 28 | end 29 | puts(' ') 30 | 31 | puts('') 32 | end 33 | end 34 | 35 | class PlainTextFormatter 36 | def output_report(context) 37 | puts("***** #{context.title} *****") 38 | context.text.each do |line| 39 | puts(line) 40 | end 41 | end 42 | end 43 | 44 | -------------------------------------------------------------------------------- /04/ex3_sans_superclass_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'stringio' 5 | 6 | require_relative 'ex3_sans_superclass' 7 | 8 | 9 | class NoSuperclassFormatterTest < Test::Unit::TestCase 10 | 11 | def test_html_report 12 | r = Report.new(HTMLFormatter.new) 13 | 14 | output = capture_output { r.output_report } 15 | assert // =~ output 16 | assert /Monthly/ =~ output 17 | assert /<p>Things/ =~ output 18 | end 19 | 20 | def test_plain_report 21 | r = Report.new(PlainTextFormatter.new) 22 | 23 | output = capture_output { r.output_report } 24 | assert /Monthly/ =~ output 25 | assert /Things/ =~ output 26 | end 27 | 28 | def capture_output(&block) 29 | output = StringIO.new 30 | begin 31 | $stdout = output 32 | block.call 33 | ensure 34 | $stdout = STDOUT 35 | end 36 | output.string 37 | end 38 | 39 | end 40 | 41 | 42 | -------------------------------------------------------------------------------- /04/ex5_strategy_proc.rb: -------------------------------------------------------------------------------- 1 | 2 | class Report 3 | attr_reader :title, :text 4 | attr_accessor :formatter 5 | 6 | def initialize(&formatter) 7 | @title = 'Monthly Report' 8 | @text = [ 'Things are going', 'really, really well.' ] 9 | @formatter = formatter 10 | end 11 | 12 | def output_report 13 | @formatter.call( self ) 14 | end 15 | end 16 | 17 | 18 | HTML_FORMATTER = lambda do |context| 19 | puts('<html>') 20 | puts(' <head>') 21 | puts(" <title>#{context.title}") 22 | puts(' ') 23 | puts(' ') 24 | context.text.each do |line| 25 | puts("

#{line}

" ) 26 | end 27 | puts(' ') 28 | puts('') 29 | end 30 | 31 | 32 | PLAIN_FORMATTER = lambda do |context| 33 | puts("***** #{context.title} *****") 34 | context.text.each do |line| 35 | puts(line) 36 | end 37 | end 38 | 39 | 40 | -------------------------------------------------------------------------------- /04/ex5_strategy_proc_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex5_strategy_proc' 6 | 7 | example %q{ 8 | 9 | report = Report.new &HTML_FORMATTER 10 | report.output_report 11 | } 12 | 13 | 14 | example %q{ 15 | 16 | report = Report.new do |context| 17 | puts("***** #{context.title} *****") 18 | context.text.each do |line| 19 | puts(line) 20 | end 21 | end 22 | 23 | 24 | report.output_report 25 | } 26 | -------------------------------------------------------------------------------- /04/ex5_strategy_proc_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'stringio' 5 | 6 | require_relative 'ex5_strategy_proc' 7 | 8 | 9 | class ProcFormatterTest < Test::Unit::TestCase 10 | 11 | def test_html_report 12 | r = Report.new &HTML_FORMATTER 13 | 14 | output = capture_output { r.output_report } 15 | assert // =~ output 16 | assert /Monthly/ =~ output 17 | assert /<p>Things/ =~ output 18 | end 19 | 20 | def test_plain_report 21 | r = Report.new &PLAIN_FORMATTER 22 | 23 | output = capture_output { r.output_report } 24 | assert /Monthly/ =~ output 25 | assert /Things/ =~ output 26 | end 27 | 28 | def capture_output(&block) 29 | output = StringIO.new 30 | begin 31 | $stdout = output 32 | block.call 33 | ensure 34 | $stdout = STDOUT 35 | end 36 | output.string 37 | end 38 | 39 | end 40 | -------------------------------------------------------------------------------- /04/ex6_sort_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex5_strategy_proc' 6 | 7 | example %q$ 8 | 9 | a = ['russell', 'mike', 'john', 'dan', 'rob'] 10 | 11 | 12 | pp a.sort 13 | 14 | pp a.sort {|a,b| a.length <=> b.length} 15 | $ 16 | -------------------------------------------------------------------------------- /05/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /05/data.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="ISO-8859-1"?> 2 | <some_data> 3 | <if> 4 | <its> 5 | <not> 6 | <one> 7 | thing, it's another 8 | </one> 9 | </not> 10 | </its> 11 | </if> 12 | </some_data> 13 | -------------------------------------------------------------------------------- /05/ex10_consistent.rb: -------------------------------------------------------------------------------- 1 | require_relative 'ex6_std_observer' 2 | 3 | 4 | class Employee 5 | 6 | def salary=(new_salary) 7 | old_salary = @salary 8 | @salary = new_salary 9 | changed if old_salary != new_salary 10 | end 11 | 12 | def title=(new_title) 13 | old_title = @title 14 | @title = new_title 15 | changed if old_title != new_title 16 | end 17 | 18 | def changes_complete 19 | notify_observers(self) 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /05/ex10_consistent_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex10_consistent' 5 | 6 | example %q{ 7 | fred = Employee.new("Fred", "Crane Operator", 30000) 8 | 9 | fred.salary = 1000000 10 | # Warning! Inconsistent state here! 11 | fred.title = 'Vice President of Sales' 12 | 13 | } 14 | 15 | 16 | example %q{ 17 | fred = Employee.new("Fred", "Crane Operator", 30000) 18 | 19 | 20 | 21 | # Don't inform the observers just yet 22 | 23 | fred.salary = 1000000 24 | fred.title = 'Vice President of Sales' 25 | 26 | # Now inform the observers! 27 | 28 | fred.changes_complete 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /05/ex10_consistent_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex10_consistent' 7 | 8 | class Observer 9 | 10 | attr_reader :subjects 11 | 12 | def initialize 13 | @subjects = [] 14 | end 15 | 16 | def update(subject) 17 | @subjects << subject 18 | end 19 | end 20 | 21 | 22 | class ConsistentObserverTest < Test::Unit::TestCase 23 | 24 | def test_consistent_changes 25 | fred = Employee.new("fred", "title", 30000) 26 | observer = Observer.new 27 | fred.add_observer(observer) 28 | 29 | fred.salary = 60000 30 | assert_equal 0, observer.subjects.size 31 | 32 | fred.salary = 'president' 33 | assert_equal 0, observer.subjects.size 34 | 35 | fred.changes_complete 36 | assert_equal fred, observer.subjects[0] 37 | end 38 | 39 | 40 | end 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /05/ex11_activerecord.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'activerecord' 3 | 4 | class EmployeeObserver < ActiveRecord::Observer 5 | def after_create(employee) 6 | # New employee record created. 7 | end 8 | 9 | def after_update(employee) 10 | # Employee record updated 11 | end 12 | 13 | def after_destroy(employee) 14 | # Employee record deleted. 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /05/ex12_xml_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | require 'rexml/parsers/sax2parser' 8 | require 'rexml/sax2listener' 9 | 10 | # 11 | # Create an XML parser for our data 12 | # 13 | xml = File.read('data.xml') 14 | parser = REXML::Parsers::SAX2Parser.new( xml ) 15 | 16 | # 17 | # Add some observers to listen for start and end elements... 18 | # 19 | parser.listen( :start_element ) do |uri, local, qname, attrs| 20 | puts "start element: #{local}" 21 | end 22 | 23 | parser.listen( :end_element ) do |uri, local, qname| 24 | puts "end element #{local}" 25 | end 26 | 27 | # 28 | # Parse the XML 29 | # 30 | parser.parse 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /05/ex1_simple_emp.rb: -------------------------------------------------------------------------------- 1 | class Employee 2 | attr_reader :name 3 | attr_accessor :title, :salary 4 | 5 | def initialize( name, title, salary ) 6 | @name = name 7 | @title = title 8 | @salary = salary 9 | end 10 | end 11 | 12 | -------------------------------------------------------------------------------- /05/ex1_simple_emp_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | 4 | require_relative '../example' 5 | require_relative 'ex1_simple_emp' 6 | 7 | example %q{ 8 | fred = Employee.new("Fred Flintstone", "Crane Operator", 30000.0) 9 | 10 | # Give Fred a raise. 11 | 12 | fred.salary=35000.0 13 | } 14 | -------------------------------------------------------------------------------- /05/ex2_hardcoded.rb: -------------------------------------------------------------------------------- 1 | class Payroll 2 | def update( changed_employee ) 3 | puts "Cut a new check for #{changed_employee.name}!" 4 | puts "His salary is now #{changed_employee.salary}!" 5 | end 6 | end 7 | 8 | class Employee 9 | attr_reader :name, :title 10 | attr_reader :salary 11 | 12 | def initialize( name, title, salary, payroll) 13 | @name = name 14 | @title = title 15 | @salary = salary 16 | @payroll = payroll 17 | end 18 | 19 | def salary=(new_salary) 20 | @salary = new_salary 21 | @payroll.update(self) 22 | end 23 | end 24 | 25 | -------------------------------------------------------------------------------- /05/ex2_hardcoded_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | 4 | require_relative '../example' 5 | require_relative 'ex2_hardcoded' 6 | 7 | example %q{ 8 | payroll = Payroll.new 9 | fred = Employee.new('Fred', 'Crane Operator', 30000, payroll) 10 | fred.salary = 35000 11 | } 12 | -------------------------------------------------------------------------------- /05/ex3_employee_obs.rb: -------------------------------------------------------------------------------- 1 | class Payroll 2 | def update( changed_employee ) 3 | puts "Cut a new check for #{changed_employee.name}!" 4 | puts "His salary is now #{changed_employee.salary}!" 5 | end 6 | end 7 | 8 | class Employee 9 | 10 | attr_accessor :name, :title, :salary 11 | 12 | def initialize( name, title, salary ) 13 | @name = name 14 | @title = title 15 | @salary = salary 16 | @observers = [] 17 | end 18 | 19 | def salary=(new_salary) 20 | @salary = new_salary 21 | notify_observers 22 | end 23 | 24 | def notify_observers 25 | @observers.each do |observer| 26 | observer.update(self) 27 | end 28 | end 29 | 30 | def add_observer(observer) 31 | @observers << observer 32 | end 33 | 34 | def delete_observer(observer) 35 | @observers.delete(observer) 36 | end 37 | 38 | end 39 | -------------------------------------------------------------------------------- /05/ex3_employee_obs_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | 4 | require_relative '../example' 5 | require_relative 'ex3_employee_obs' 6 | 7 | example %q{ 8 | fred = Employee.new('Fred', 'Crane Operator', 30000.0) 9 | 10 | payroll = Payroll.new 11 | fred.add_observer( payroll ) 12 | 13 | fred.salary=35000.0 14 | 15 | 16 | 17 | 18 | 19 | class TaxMan 20 | def update( changed_employee ) 21 | puts "Send #{changed_employee.name} a new tax bill!" 22 | end 23 | end 24 | 25 | tax_man = TaxMan.new 26 | fred.add_observer(tax_man) 27 | 28 | fred.salary=90000.0 29 | 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /05/ex4_subject_class.rb: -------------------------------------------------------------------------------- 1 | 2 | class Subject 3 | def initialize 4 | @observers=[] 5 | end 6 | 7 | def add_observer(observer) 8 | @observers << observer 9 | end 10 | 11 | def delete_observer(observer) 12 | @observers.delete(observer) 13 | end 14 | 15 | def notify_observers 16 | @observers.each do |observer| 17 | observer.update(self) 18 | end 19 | end 20 | end 21 | 22 | class Employee < Subject 23 | attr_reader :name, :address 24 | attr_reader :salary 25 | 26 | def initialize( name, title, salary) 27 | super() 28 | @name = name 29 | @title = title 30 | @salary = salary 31 | end 32 | 33 | def salary=(new_salary) 34 | @salary = new_salary 35 | notify_observers 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /05/ex4_subject_class_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | require 'stringio' 6 | 7 | require_relative 'ex4_subject_class' 8 | require_relative 'ex4_subject_test_methods_module' 9 | 10 | class SubjectClassTest < Test::Unit::TestCase 11 | include SubjectTestMethods 12 | end 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /05/ex5_subject_module.rb: -------------------------------------------------------------------------------- 1 | 2 | module Subject 3 | def initialize 4 | @observers=[] 5 | end 6 | 7 | def add_observer(observer) 8 | @observers << observer 9 | end 10 | 11 | def delete_observer(observer) 12 | @observers.delete(observer) 13 | end 14 | 15 | def notify_observers 16 | @observers.each do |observer| 17 | observer.update(self) 18 | end 19 | end 20 | end 21 | 22 | class Employee 23 | include Subject 24 | 25 | attr_reader :name, :address 26 | attr_reader :salary 27 | 28 | def initialize( name, title, salary) 29 | super() 30 | @name = name 31 | @title = title 32 | @salary = salary 33 | end 34 | 35 | def salary=(new_salary) 36 | @salary = new_salary 37 | notify_observers 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /05/ex5_subject_module_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | require 'stringio' 6 | 7 | require_relative 'ex5_subject_module' 8 | 9 | # 10 | # Use the same test methods as ex4 11 | # 12 | require_relative 'ex4_subject_test_methods_module' 13 | 14 | class SubjectClassTest < Test::Unit::TestCase 15 | include SubjectTestMethods 16 | end 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /05/ex6_std_observer.rb: -------------------------------------------------------------------------------- 1 | require 'observer' 2 | 3 | class Employee 4 | include Observable 5 | 6 | attr_reader :name, :address 7 | attr_reader :salary 8 | 9 | def initialize( name, title, salary) 10 | @name = name 11 | @title = title 12 | @salary = salary 13 | end 14 | 15 | def salary=(new_salary) 16 | @salary = new_salary 17 | changed 18 | notify_observers(self) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /05/ex6_std_observer_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | require 'stringio' 6 | 7 | require_relative 'ex6_std_observer' 8 | 9 | # 10 | # Use the same test methods as ex4 11 | # 12 | require_relative 'ex4_subject_test_methods_module' 13 | 14 | class StandardObserverTest < Test::Unit::TestCase 15 | include SubjectTestMethods 16 | end 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /05/ex7_observer_proc.rb: -------------------------------------------------------------------------------- 1 | 2 | module Subject 3 | def initialize 4 | @observers=[] 5 | end 6 | 7 | def add_observer(&observer) 8 | @observers << observer 9 | end 10 | 11 | def delete_observer(&observer) 12 | @observers.delete(observer) 13 | end 14 | 15 | def notify_observers 16 | @observers.each do |observer| 17 | observer.call(self) 18 | end 19 | end 20 | end 21 | 22 | 23 | class Employee 24 | include Subject 25 | 26 | attr_accessor :name, :title, :salary 27 | 28 | def initialize( name, title, salary ) 29 | super() 30 | @name = name 31 | @title = title 32 | @salary = salary 33 | end 34 | 35 | def salary=(new_salary) 36 | @salary = new_salary 37 | notify_observers 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /05/ex7_observer_proc_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | 4 | require_relative '../example' 5 | require_relative 'ex7_observer_proc' 6 | 7 | example %q{ 8 | 9 | fred = Employee.new('Fred', 'Crane Operator', 30000) 10 | 11 | fred.add_observer do |changed_employee| 12 | puts "Cut a new check for #{changed_employee.name}!" 13 | puts "His salary is now #{changed_employee.salary}!" 14 | end 15 | 16 | fred.salary = 40000 17 | } 18 | -------------------------------------------------------------------------------- /05/ex8_variations_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'pp' 4 | 5 | require_relative '../example' 6 | require_relative 'ex7_observer_proc' 7 | 8 | class DummyObserver 9 | 10 | def update(*args) 11 | puts "update called with: " 12 | pp args 13 | end 14 | 15 | def update_salary(*args) 16 | puts "update salary called with: " 17 | pp args 18 | end 19 | 20 | def update_title(*args) 21 | puts "update title called with: " 22 | pp args 23 | end 24 | end 25 | 26 | example %q{ 27 | 28 | old_salary = 50000 29 | new_salary = 60000 30 | observer = DummyObserver.new 31 | old_title = 'janitor' 32 | new_title = 'vice president' 33 | 34 | observer.update(self, :salary_changed, old_salary, new_salary) 35 | 36 | observer.update_salary(self, old_salary, new_salary) 37 | 38 | observer.update_title(self, old_title, new_title) 39 | } 40 | -------------------------------------------------------------------------------- /05/ex9_chatter.rb: -------------------------------------------------------------------------------- 1 | require_relative 'ex6_std_observer' 2 | 3 | 4 | class Employee 5 | 6 | 7 | def salary=(new_salary) 8 | old_salary = @salary 9 | @salary = new_salary 10 | if old_salary != new_salary 11 | changed 12 | notify_observers(self) 13 | end 14 | end 15 | 16 | 17 | def title=(new_title) 18 | old_title = @title 19 | @title = new_title 20 | if old_title != new_title 21 | changed = true 22 | notify_observers(self) 23 | end 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /05/ex9_chatter_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex9_chatter' 7 | 8 | class Observer 9 | 10 | attr_reader :subjects 11 | 12 | def initialize 13 | @subjects = [] 14 | end 15 | 16 | def update(subject) 17 | @subjects << subject 18 | end 19 | end 20 | 21 | 22 | class LessChattyObserverTest < Test::Unit::TestCase 23 | 24 | def test_salary_chattyness 25 | fred = Employee.new("fred", "title", 30000) 26 | observer = Observer.new 27 | fred.add_observer(observer) 28 | 29 | fred.salary = 30000 30 | assert_equal 0, observer.subjects.size 31 | 32 | fred.salary = 30001 33 | assert_equal fred, observer.subjects[0] 34 | end 35 | 36 | def test_title_chattyness 37 | fred = Employee.new("fred", "title", 30000) 38 | observer = Observer.new 39 | fred.add_observer(observer) 40 | 41 | fred.title = 'title' 42 | assert_equal 0, observer.subjects.size 43 | 44 | fred.salary = 'president' 45 | assert_equal fred, observer.subjects[0] 46 | end 47 | 48 | 49 | end 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /06/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /06/background.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/russolsen/design_patterns_in_ruby_code/f62eaf1cd9d53d68ba6a87e0f9b81d635ee89ec2/06/background.gif -------------------------------------------------------------------------------- /06/ex10_ancestry_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | # 6 | # Simple demo of walking the ancestry tree 7 | # 8 | 9 | require_relative 'ex1_task' 10 | # 11 | # Task / Composite with parent pointers 12 | # 13 | 14 | example(%q{ 15 | require_relative 'ex9_parent' 16 | 17 | c1 = CompositeTask.new('grandpa') 18 | c2 = CompositeTask.new('dad') 19 | c3 = CompositeTask.new('dad') 20 | 21 | c1.add_sub_task(c2) 22 | c2.add_sub_task(c3) 23 | 24 | task = c3 25 | while task # while the task is not null 26 | puts "task: #{task}" 27 | task = task.parent 28 | end 29 | }, __FILE__) 30 | -------------------------------------------------------------------------------- /06/ex11_abuse_child.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Demo how to count leaf nodes 3 | # 4 | require_relative 'ex1_task' 5 | require_relative 'ex4_comp_task' 6 | 7 | # 8 | # Wrong way of doing it 9 | # 10 | class CompositeTask < Task 11 | 12 | # Lots of code omitted ... 13 | 14 | def total_number_basic_tasks 15 | return @sub_tasks.length 16 | end 17 | 18 | end 19 | 20 | 21 | -------------------------------------------------------------------------------- /06/ex11_abuse_child_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex11_abuse_child' 7 | 8 | 9 | 10 | class AbuseChildTest < Test::Unit::TestCase 11 | 12 | 13 | def test_add_delete 14 | c1 = CompositeTask.new('grandpa') 15 | c2 = CompositeTask.new('dad') 16 | c2.add_sub_task(MixTask.new) 17 | c2.add_sub_task(MixTask.new) 18 | c2.add_sub_task(MixTask.new) 19 | 20 | c1.add_sub_task(c2) 21 | c1.add_sub_task(MixTask.new) 22 | 23 | # 24 | # This should be four, but we are demoing the mistake. 25 | # 26 | assert_equal 2, c1.total_number_basic_tasks 27 | 28 | end 29 | 30 | 31 | end 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /06/ex12_abuse_child.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Demo how to count leaf nodes 3 | # 4 | require_relative 'ex1_task' 5 | require_relative 'ex4_comp_task' 6 | 7 | # 8 | # Right way of doing it 9 | # 10 | 11 | class Task 12 | # Lots of code omitted ... 13 | 14 | def total_number_basic_tasks 15 | 1 16 | end 17 | end 18 | 19 | class CompositeTask < Task 20 | 21 | # Lots of code omitted ... 22 | 23 | def total_number_basic_tasks 24 | total = 0 25 | @sub_tasks.each {|task| total += task.total_number_basic_tasks} 26 | total 27 | end 28 | 29 | end 30 | 31 | 32 | -------------------------------------------------------------------------------- /06/ex12_abuse_child_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex12_abuse_child' 7 | 8 | 9 | 10 | class AbuseChildTest < Test::Unit::TestCase 11 | 12 | 13 | def test_add_delete 14 | c1 = CompositeTask.new('grandpa') 15 | c2 = CompositeTask.new('dad') 16 | c2.add_sub_task(MixTask.new) 17 | c2.add_sub_task(MixTask.new) 18 | c2.add_sub_task(MixTask.new) 19 | 20 | c1.add_sub_task(c2) 21 | c1.add_sub_task(MixTask.new) 22 | 23 | # 24 | # This is four, the correct answer 25 | # 26 | assert_equal 4, c1.total_number_basic_tasks 27 | 28 | end 29 | 30 | 31 | end 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /06/ex1_task.rb: -------------------------------------------------------------------------------- 1 | class Task 2 | attr_reader :name 3 | 4 | def initialize(name) 5 | @name = name 6 | end 7 | 8 | def get_time_required 9 | 0.0 10 | end 11 | end 12 | 13 | class AddDryIngredientsTask < Task 14 | 15 | def initialize 16 | super('Add dry ingredients') 17 | end 18 | 19 | def get_time_required 20 | 1.0 # A minute to add flour and sugar 21 | end 22 | end 23 | 24 | class MixTask < Task 25 | 26 | def initialize 27 | super('Mix that batter up!') 28 | end 29 | 30 | def get_time_required 31 | 3.0 # Mix for 3 minutes 32 | end 33 | end 34 | 35 | class AddLiquidsTask < Task 36 | 37 | def initialize 38 | super('Add Liquids') 39 | end 40 | 41 | def get_time_required 42 | 4.0 43 | end 44 | end 45 | 46 | -------------------------------------------------------------------------------- /06/ex1_task_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_task' 7 | 8 | 9 | 10 | class TaskTest < Test::Unit::TestCase 11 | 12 | def test_task 13 | task = Task.new('task') 14 | assert_equal 'task', task.name 15 | assert_equal 0, task.get_time_required 16 | end 17 | 18 | def test_add_dry_task 19 | task = AddDryIngredientsTask.new 20 | assert_equal 'Add dry ingredients', task.name 21 | assert_equal 1, task.get_time_required 22 | end 23 | 24 | def test_mix_task 25 | task = MixTask.new 26 | assert_equal 3, task.get_time_required 27 | end 28 | 29 | def test_add_liq_task 30 | task = AddLiquidsTask.new 31 | assert_equal 4, task.get_time_required 32 | end 33 | 34 | end 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /06/ex3_make_batter.rb: -------------------------------------------------------------------------------- 1 | require_relative 'ex1_task' 2 | 3 | class MakeBatterTask < Task 4 | def initialize 5 | super('Make batter') 6 | @sub_tasks = [] 7 | add_sub_task( AddDryIngredientsTask.new ) 8 | add_sub_task( AddLiquidsTask.new ) 9 | add_sub_task( MixTask.new ) 10 | end 11 | 12 | def add_sub_task(task) 13 | @sub_tasks << task 14 | end 15 | 16 | def remove_sub_task(task) 17 | @sub_tasks.delete(task) 18 | end 19 | 20 | def get_time_required 21 | time=0.0 22 | @sub_tasks.each {|task| time += task.get_time_required} 23 | time 24 | end 25 | end 26 | 27 | -------------------------------------------------------------------------------- /06/ex3_make_batter_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_task' 7 | require_relative 'ex3_make_batter' 8 | 9 | 10 | 11 | class MakeBatterTest < Test::Unit::TestCase 12 | 13 | 14 | def test_make_batter 15 | t = MakeBatterTask.new 16 | assert_equal t.name, 'Make batter' 17 | subtasks = t.instance_variable_get(:@sub_tasks).clone 18 | assert_equal 3, subtasks.size 19 | assert_equal 8, t.get_time_required 20 | t.remove_sub_task subtasks[0] 21 | t.remove_sub_task subtasks[1] 22 | t.remove_sub_task subtasks[2] 23 | assert_equal 0, t.get_time_required 24 | end 25 | 26 | 27 | end 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /06/ex4_comp_task.rb: -------------------------------------------------------------------------------- 1 | class CompositeTask < Task 2 | def initialize(name) 3 | super(name) 4 | @sub_tasks = [] 5 | end 6 | 7 | def add_sub_task(task) 8 | @sub_tasks << task 9 | end 10 | 11 | def remove_sub_task(task) 12 | @sub_tasks.delete(task) 13 | end 14 | 15 | def get_time_required 16 | time=0.0 17 | @sub_tasks.each {|task| time += task.get_time_required} 18 | time 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /06/ex5_make_batter.rb: -------------------------------------------------------------------------------- 1 | 2 | class MakeBatterTask < CompositeTask 3 | def initialize 4 | super('Make batter') 5 | add_sub_task( AddDryIngredientsTask.new ) 6 | add_sub_task( AddLiquidsTask.new ) 7 | add_sub_task( MixTask.new ) 8 | end 9 | end 10 | 11 | -------------------------------------------------------------------------------- /06/ex5_make_batter_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_task' 7 | require_relative 'ex4_comp_task' 8 | require_relative 'ex5_make_batter' 9 | 10 | 11 | class MakeBatterTest < Test::Unit::TestCase 12 | def test_add_delete 13 | task = MakeBatterTask.new 14 | assert_equal 8, task.get_time_required 15 | end 16 | end 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /06/ex6_make_cake.rb: -------------------------------------------------------------------------------- 1 | 2 | class FillPanTask < Task 3 | 4 | def initialize 5 | super('Fill pan') 6 | end 7 | 8 | def get_time_required 9 | 2 10 | end 11 | 12 | end 13 | 14 | class FrostTask < Task 15 | 16 | def initialize 17 | super('Frost') 18 | end 19 | 20 | def get_time_required 21 | 2 22 | end 23 | 24 | end 25 | 26 | class BakeTask < Task 27 | 28 | def initialize 29 | super('Bake') 30 | end 31 | 32 | def get_time_required 33 | 2 34 | end 35 | 36 | end 37 | 38 | class LickSpoonTask < Task 39 | 40 | def initialize 41 | super('Bake') 42 | end 43 | 44 | def get_time_required 45 | 100 46 | end 47 | 48 | end 49 | 50 | 51 | class MakeCakeTask < CompositeTask 52 | def initialize 53 | super('Make cake') 54 | add_sub_task( MakeBatterTask.new ) 55 | add_sub_task( FillPanTask.new ) 56 | add_sub_task( BakeTask.new ) 57 | add_sub_task( FrostTask.new ) 58 | add_sub_task( LickSpoonTask.new ) 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /06/ex6_make_cake_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_task' 7 | require_relative 'ex4_comp_task' 8 | require_relative 'ex3_make_batter' 9 | require_relative 'ex6_make_cake' 10 | 11 | 12 | 13 | class MakeCakeTest < Test::Unit::TestCase 14 | 15 | def setup 16 | @cake_task = MakeCakeTask.new 17 | end 18 | 19 | def test_time_required 20 | assert_equal 114, @cake_task.get_time_required 21 | end 22 | 23 | def test_size 24 | class << @cake_task 25 | def size 26 | @sub_tasks.size 27 | end 28 | end 29 | assert_equal 5, @cake_task.size 30 | end 31 | 32 | end 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /06/ex7_operators.rb: -------------------------------------------------------------------------------- 1 | require 'pp' 2 | require_relative 'ex1_task' 3 | 4 | class CompositeTask < Task 5 | 6 | def initialize(name) 7 | super(name) 8 | @sub_tasks = [] 9 | end 10 | 11 | def <<(task) 12 | @sub_tasks << task 13 | end 14 | 15 | def [](index) 16 | @sub_tasks[index] 17 | end 18 | 19 | def []=(index, new_value) 20 | @sub_tasks[index] = new_value 21 | end 22 | 23 | def size 24 | @sub_tasks.size 25 | end 26 | 27 | def remove_sub_task(task) 28 | @sub_tasks.delete(task) 29 | end 30 | 31 | def get_time_required 32 | time=0.0 33 | @sub_tasks.each {|task| time += task.get_time_required} 34 | time 35 | end 36 | 37 | end 38 | 39 | class MakeBatterTask < CompositeTask 40 | 41 | def initialize 42 | super('Make batter') 43 | self << AddDryIngredientsTask.new 44 | self << AddLiquidsTask.new 45 | self << MixTask.new 46 | end 47 | 48 | end 49 | -------------------------------------------------------------------------------- /06/ex7_operators_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | # 6 | # Simple demo of composite with operators 7 | # 8 | require_relative 'ex7_operators' 9 | require 'pp' 10 | 11 | example %q{ 12 | 13 | composite = CompositeTask.new('example') 14 | composite << MixTask.new 15 | 16 | pp composite 17 | 18 | puts composite[0].get_time_required 19 | composite[1] = AddDryIngredientsTask.new 20 | 21 | pp composite 22 | } 23 | -------------------------------------------------------------------------------- /06/ex8_array.rb: -------------------------------------------------------------------------------- 1 | require_relative 'ex1_task' 2 | 3 | class CompositeTask < Array 4 | attr_reader :name 5 | 6 | def initialize(name) 7 | @name = name 8 | end 9 | 10 | def get_time_required 11 | time=0.0 12 | each {|task| time += task.get_time_required} 13 | time 14 | end 15 | end 16 | 17 | class MakeBatterTask < CompositeTask 18 | def initialize 19 | super('Make batter') 20 | self << AddDryIngredientsTask.new 21 | self << AddLiquidsTask.new 22 | self << MixTask.new 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /06/ex9_parent.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Task with parent pointers. 3 | # 4 | 5 | class Task 6 | attr_accessor :name, :parent 7 | 8 | def initialize(name) 9 | @name = name 10 | @parent = nil 11 | end 12 | 13 | def get_time_required 14 | 0.0 15 | end 16 | end 17 | 18 | 19 | 20 | class CompositeTask < Task 21 | def initialize(name) 22 | super(name) 23 | @sub_tasks = [] 24 | end 25 | 26 | def add_sub_task(task) 27 | @sub_tasks << task 28 | task.parent = self 29 | end 30 | 31 | def remove_sub_task(task) 32 | @sub_tasks.delete(task) 33 | task.parent = nil 34 | end 35 | 36 | def get_time_required 37 | time=0.0 38 | @sub_tasks.each {|task| time += task.get_time_required} 39 | time 40 | end 41 | end 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /07/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /07/ex10_scan_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example.rb' 4 | 5 | example %q{ 6 | 7 | s = 'Peter piper picked a peck of pickled peppers' 8 | s.scan(/[Pp]\w*/) { |word| puts "The word is #{word}" } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /07/ex1_array_iterator.rb: -------------------------------------------------------------------------------- 1 | class ArrayIterator 2 | def initialize(array) 3 | @array = array 4 | @index = 0 5 | end 6 | 7 | def has_next? 8 | @index < @array.length 9 | end 10 | 11 | def item 12 | @array[@index] 13 | end 14 | 15 | def next_item 16 | value = @array[@index] 17 | @index += 1 18 | value 19 | end 20 | end 21 | 22 | 23 | -------------------------------------------------------------------------------- /07/ex1_array_iterator_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_array_iterator' 7 | 8 | 9 | 10 | class ArrayIteratorTest < Test::Unit::TestCase 11 | 12 | 13 | def test_iterator 14 | array = ['aaa', 'bbb', 'ccc'] 15 | 16 | i = ArrayIterator.new(array) 17 | 18 | assert_equal( 'aaa', i.item ) 19 | assert_equal( 'aaa', i.next_item ) 20 | assert i.has_next? 21 | 22 | assert_equal( 'bbb', i.item ) 23 | assert_equal( 'bbb', i.next_item ) 24 | assert i.has_next? 25 | 26 | assert_equal( 'ccc', i.item ) 27 | assert_equal( 'ccc', i.next_item ) 28 | assert (not i.has_next?) 29 | 30 | end 31 | 32 | def test_empty_array_iterator 33 | array = [] 34 | i = ArrayIterator.new(array) 35 | assert (not i.has_next?) 36 | end 37 | 38 | 39 | end 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /07/ex2_array_iter_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex1_array_iterator' 6 | 7 | example %q{ 8 | 9 | array = ['red', 'green', 'blue'] 10 | 11 | i = ArrayIterator.new(array) 12 | while i.has_next? 13 | puts "item: #{i.next_item}" 14 | end 15 | } 16 | -------------------------------------------------------------------------------- /07/ex3_string_iter_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex1_array_iterator' 6 | 7 | example %q{ 8 | i = ArrayIterator.new('abc') 9 | 10 | while i.has_next? 11 | puts "item: #{i.next_item.chr}" 12 | end 13 | } 14 | 15 | -------------------------------------------------------------------------------- /07/ex4_for_each_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | def for_each_element(array) 8 | i = 0 9 | while i < array.length 10 | yield(array[i]) 11 | i += 1 12 | end 13 | end 14 | 15 | 16 | a = [10, 20, 30] 17 | for_each_element(a) {|element| puts "The element is #{element}"} 18 | } 19 | 20 | 21 | example %q{ 22 | a = [10, 20, 30] 23 | a.each {|element| puts "The element is #{element}"} 24 | 25 | } 26 | -------------------------------------------------------------------------------- /07/ex5_merge.rb: -------------------------------------------------------------------------------- 1 | require_relative 'ex1_array_iterator' 2 | 3 | def merge(array1, array2) 4 | merged = [] 5 | 6 | iterator1 = ArrayIterator.new(array1) 7 | iterator2 = ArrayIterator.new(array2) 8 | 9 | while( iterator1.has_next? and iterator2.has_next? ) 10 | if iterator1.item < iterator2.item 11 | merged << iterator1.next_item 12 | else 13 | merged << iterator2.next_item 14 | end 15 | end 16 | 17 | # Pick up the leftovers from array1 18 | 19 | while( iterator1.has_next?) 20 | merged << iterator1.next_item 21 | end 22 | 23 | # Pick up the leftovers from array2 24 | 25 | while( iterator2.has_next?) 26 | merged << iterator2.next_item 27 | end 28 | 29 | merged 30 | end 31 | -------------------------------------------------------------------------------- /07/ex5_merge_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex5_merge' 7 | 8 | 9 | 10 | class ArrayIteratorTest < Test::Unit::TestCase 11 | 12 | 13 | def test_typical_merge 14 | a1 = [-999, -1, 0, 10, 20 ] 15 | a2 = [-3, 1, 2, 999 ] 16 | result = check_merge(a1, a2) 17 | end 18 | 19 | def test_empty_merge 20 | check_merge([], []) 21 | end 22 | 23 | def test_edge_cases 24 | check_merge([1], []) 25 | check_merge([], [1]) 26 | check_merge([1], [1]) 27 | check_merge([1], [2]) 28 | check_merge([2], [1]) 29 | check_merge([1, 2, 3], [101, 999]) 30 | check_merge([101, 999], [1, 2, 3]) 31 | end 32 | 33 | def check_merge(a1, a2) 34 | result = merge(a1, a2) 35 | assert_equal( a1.size + a2.size, result.size) 36 | a1.each {|element| assert(result.member?(element))} 37 | a2.each {|element| assert(result.member?(element))} 38 | (result.size-1).times { |i| assert( result[i] <= result[i+1] ) } 39 | assert_equal (a1 + a2).sort, result 40 | end 41 | 42 | end 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /07/ex6_enum_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | a = [ 'joe', 'sam', 'george' ] 7 | result = a.all? {|element| element.length < 4} 8 | puts result 9 | 10 | result = a.any? {|element| element.length < 4} 11 | puts result 12 | } 13 | -------------------------------------------------------------------------------- /07/ex6a_enum_portfolio.rb: -------------------------------------------------------------------------------- 1 | class Account 2 | attr_accessor :name, :balance 3 | 4 | def initialize(name, balance) 5 | @name = name 6 | @balance = balance 7 | end 8 | end 9 | 10 | class Portfolio 11 | include Enumerable 12 | 13 | def initialize 14 | @accounts = [] 15 | end 16 | 17 | def each(&block) 18 | @accounts.each(&block) 19 | end 20 | 21 | def add_account(account) 22 | @accounts << account 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /07/ex6a_enum_portfolio_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex6a_enum_portfolio' 6 | 7 | example %q{ 8 | 9 | my_portfolio = Portfolio.new 10 | 11 | my_portfolio.add_account(Account.new('Bonds', 200)) 12 | my_portfolio.add_account(Account.new('Stocks', 100)) 13 | my_portfolio.add_account(Account.new('Real Estate', 1000)) 14 | 15 | my_portfolio.any? {|account| account.balance > 2000} 16 | my_portfolio.all? {|account| account.balance >= 10} 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /07/ex7_change_resis_extern.rb: -------------------------------------------------------------------------------- 1 | class ChangeResistantArrayIterator 2 | def initialize(array) 3 | @array = Array.new(array) 4 | @index = 0 5 | end 6 | 7 | def has_next? 8 | @index < @array.length 9 | end 10 | 11 | def item 12 | @array[@index] 13 | end 14 | 15 | def next_item 16 | value = @array[@index] 17 | @index += 1 18 | value 19 | end 20 | end 21 | 22 | 23 | -------------------------------------------------------------------------------- /07/ex7_change_resis_extern_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | array=['red', 'green', 'blue', 'purple'] 8 | 9 | array.each do | color | 10 | puts color 11 | if color == 'green' 12 | array.delete(color) 13 | end 14 | end 15 | 16 | } 17 | -------------------------------------------------------------------------------- /07/ex8_changed_iternal_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | array=['red', 'green', 'blue', 'purple'] 8 | 9 | array.each do | color | 10 | puts color 11 | if color == 'green' 12 | array.delete(color) 13 | end 14 | end 15 | } 16 | -------------------------------------------------------------------------------- /07/ex9_change_resis_for_each.rb: -------------------------------------------------------------------------------- 1 | 2 | def change_resistant_for_each_element(array) 3 | copy = Array.new(array) 4 | i = 0 5 | while i < copy.length 6 | yield(copy[i]) 7 | i += 1 8 | end 9 | end 10 | 11 | array=['red', 'green', 'blue', 'purple'] 12 | change_resistant_for_each_element(array) do | color | 13 | puts color 14 | if color == 'green' 15 | array.delete(color) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /07/ex9_change_resis_for_each_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex9_change_resis_for_each' 7 | 8 | 9 | 10 | class ChangeResistantForEachTest < Test::Unit::TestCase 11 | 12 | 13 | def test_iterator 14 | array = ['red', 'green', 'blue', 'purple'] 15 | 16 | i = 0 17 | change_resistant_for_each_element(array) do |color| 18 | assert_equal array[i], color 19 | i += 1 20 | end 21 | 22 | end 23 | 24 | def test_change_resistance 25 | array = ['red', 'green', 'blue', 'purple'] 26 | reference_array = array.clone 27 | 28 | i = 0 29 | change_resistant_for_each_element(array) do |color| 30 | array.delete('red') if i == 0 31 | array.delete('green') if i == 0 32 | assert_equal reference_array[i], color 33 | i += 1 34 | end 35 | 36 | end 37 | 38 | 39 | end 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /07/names.txt: -------------------------------------------------------------------------------- 1 | Fred 2 | George 3 | Billy 4 | Harry 5 | -------------------------------------------------------------------------------- /07/short.txt: -------------------------------------------------------------------------------- 1 | It was a dark and stormy night. 2 | -------------------------------------------------------------------------------- /08/001_create_book_table.rb: -------------------------------------------------------------------------------- 1 | class CreateBookTable < ActiveRecord::Migration 2 | def self.up 3 | create_table :books do |t| 4 | t.column :title, :string 5 | t.column :author, :string 6 | end 7 | end 8 | 9 | def self.down 10 | drop_table :books 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /08/002_add_isbn_column.rb: -------------------------------------------------------------------------------- 1 | class AddIsbnColumn < ActiveRecord::Migration 2 | def self.up 3 | add_column :books, 'isbn', :string 4 | end 5 | 6 | def self.down 7 | remove_column :books, 'isbn' 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /08/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | 3 | task :clean do 4 | rm_rf 'employees' 5 | end 6 | -------------------------------------------------------------------------------- /08/ex1_slickbutton_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example %q{ 5 | 6 | class SlickButton 7 | # 8 | # Lots of button drawing and management 9 | # code omitted... 10 | # 11 | 12 | def on_button_push 13 | # 14 | # Do something when the button is pushed. 15 | # 16 | end 17 | end 18 | 19 | } 20 | 21 | example %q{ 22 | class SaveButton < SlickButton 23 | def on_button_push 24 | # 25 | # Save the current document... 26 | # 27 | end 28 | end 29 | 30 | class NewDocumentButton < SlickButton 31 | def on_button_push 32 | # 33 | # Create a new document... 34 | # 35 | end 36 | end 37 | } 38 | 39 | sb = SlickButton.new 40 | sb.on_button_push 41 | pp sb 42 | 43 | sb = SaveButton.new 44 | sb.on_button_push 45 | 46 | ndb = NewDocumentButton.new 47 | ndb.on_button_push 48 | 49 | -------------------------------------------------------------------------------- /08/ex2_button_with_cmd.rb: -------------------------------------------------------------------------------- 1 | class SlickButton 2 | attr_accessor :command 3 | 4 | def initialize(command) 5 | @command = command 6 | end 7 | 8 | # 9 | # Lots of button drawing and management 10 | # code omitted... 11 | # 12 | 13 | def on_button_push 14 | @command.execute if @command 15 | end 16 | end 17 | 18 | class SaveCommand 19 | def execute 20 | # 21 | # Save the current document... 22 | # 23 | end 24 | end 25 | 26 | -------------------------------------------------------------------------------- /08/ex2_button_with_cmd_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex2_button_with_cmd' 5 | 6 | example %q{ 7 | save_button = SlickButton.new( SaveCommand.new ) 8 | 9 | 10 | 11 | pp save_button 12 | 13 | } 14 | -------------------------------------------------------------------------------- /08/ex2_button_with_cmd_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex2_button_with_cmd' 7 | 8 | class MockCommand 9 | attr_reader :executed 10 | 11 | def initialize 12 | @executed = false 13 | end 14 | 15 | def execute 16 | @executed = true 17 | end 18 | end 19 | 20 | class ButtonWithCommandTest < Test::Unit::TestCase 21 | 22 | 23 | def test_button 24 | cmd = MockCommand.new 25 | sb = SlickButton.new(cmd) 26 | assert(!cmd.executed) 27 | sb.on_button_push 28 | assert cmd.executed 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /08/ex3_block_button.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class SlickButton 4 | attr_accessor :command 5 | 6 | def initialize(&block) 7 | @command = block 8 | end 9 | 10 | # 11 | # Lots of button drawing and management 12 | # code omitted... 13 | # 14 | 15 | def on_button_push 16 | @command.call if @command 17 | end 18 | end 19 | 20 | -------------------------------------------------------------------------------- /08/ex3_block_button_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex3_block_button' 5 | 6 | example %q{ 7 | new_button = SlickButton.new do 8 | # 9 | # Create a new docuument... 10 | # 11 | end 12 | 13 | new_button.on_button_push 14 | } 15 | 16 | 17 | test_button = SlickButton.new { puts "pushed" } 18 | test_button.on_button_push 19 | 20 | -------------------------------------------------------------------------------- /08/ex3_block_button_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex3_block_button' 7 | 8 | class ButtonWithBlockTest < Test::Unit::TestCase 9 | 10 | 11 | def test_button 12 | called = false 13 | sb = SlickButton.new { called = true } 14 | assert(!called) 15 | sb.on_button_push 16 | assert(called) 17 | end 18 | 19 | end 20 | 21 | -------------------------------------------------------------------------------- /08/ex4_file_cmd_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex4_file_cmd' 5 | 6 | example %q{ 7 | cmds = CompositeCommand.new 8 | 9 | cmds.add_command(CreateFile.new("file1.txt", "hello world\n")) 10 | cmds.add_command(CopyFile.new("file1.txt", "file2.txt")) 11 | cmds.add_command(DeleteFile.new("file1.txt")) 12 | 13 | cmds.execute 14 | puts cmds.description 15 | } 16 | -------------------------------------------------------------------------------- /08/ex6_overuse_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | open("foo.dat", "w") {} 6 | 7 | example %q{ 8 | 9 | class FileDeleteCommand 10 | 11 | def initialize(path) 12 | @path = path 13 | end 14 | 15 | def execute 16 | File.delete(@path) 17 | end 18 | 19 | end 20 | 21 | fdc = FileDeleteCommand.new('foo.dat') 22 | fdc.execute 23 | 24 | } 25 | 26 | open("foo.dat", "w") {} 27 | 28 | example %q{ 29 | File.delete('foo.dat') 30 | } 31 | -------------------------------------------------------------------------------- /08/existing_test_file.txt: -------------------------------------------------------------------------------- 1 | hello -------------------------------------------------------------------------------- /08/file2.txt: -------------------------------------------------------------------------------- 1 | hello world 2 | -------------------------------------------------------------------------------- /09/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /09/british_text_object.rb: -------------------------------------------------------------------------------- 1 | require_relative 'ex3_renderer' 2 | -------------------------------------------------------------------------------- /09/cypher: -------------------------------------------------------------------------------- 1 | 5HE �IE�Na LE�I �AOo EK�Io -------------------------------------------------------------------------------- /09/ex1_encrypter.rb: -------------------------------------------------------------------------------- 1 | 2 | class Encrypter 3 | def initialize(key) 4 | @key = key 5 | end 6 | 7 | def encrypt(reader, writer) 8 | key_index = 0 9 | while not reader.eof? 10 | clear_char = reader.getc 11 | encrypted_char = clear_char.ord ^ @key[key_index].ord 12 | writer.putc(encrypted_char.chr) 13 | key_index = (key_index + 1) % @key.size 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /09/ex1_encrypter_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex1_encrypter' 6 | 7 | example %q{ 8 | reader = File.open('message.txt') 9 | writer = File.open('message.encrypted','w') 10 | encrypter = Encrypter.new('my secret key') 11 | encrypter.encrypt(reader, writer) 12 | 13 | 14 | writer.close 15 | } 16 | 17 | -------------------------------------------------------------------------------- /09/ex1_encrypter_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_encrypter' 7 | 8 | class EncrypterTest < Test::Unit::TestCase 9 | 10 | def setup 11 | File.delete('cypher') if File.exists?('cypher') 12 | File.delete('new_message') if File.exists?('new_message') 13 | end 14 | 15 | def test_round_trip 16 | encrypter = Encrypter.new("a key") 17 | input = File.open('message.txt') 18 | File.open('cypher', 'w') {|output| encrypter.encrypt(input, output)} 19 | 20 | assert ! same_contents?('message.txt', 'cypher') 21 | 22 | input = File.open('cypher') 23 | File.open('new_message', 'w') {|output| encrypter.encrypt(input, output)} 24 | encrypter.encrypt(input, 'new_message') 25 | 26 | assert same_contents?('message.txt', 'new_message') 27 | end 28 | 29 | def same_contents?(path1, path2) 30 | File.read(path1) == File.read(path2) 31 | end 32 | 33 | end 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /09/ex2_ioadapter.rb: -------------------------------------------------------------------------------- 1 | class StringIOAdapter 2 | def initialize(string) 3 | @string = string 4 | @position = 0 5 | end 6 | 7 | def getc 8 | if @position >= @string.length 9 | raise EOFError 10 | end 11 | ch = @string[@position] 12 | @position += 1 13 | return ch 14 | end 15 | 16 | def eof? 17 | return @position >= @string.length 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /09/ex2_ioadapter_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex1_encrypter' 5 | require_relative 'ex2_ioadapter' 6 | 7 | example %q{ 8 | encrypter = Encrypter.new('XYZZY') 9 | reader= StringIOAdapter.new('We attack at dawn') 10 | writer=File.open('out.txt', 'w') 11 | encrypter.encrypt(reader, writer) 12 | 13 | writer.close 14 | } 15 | -------------------------------------------------------------------------------- /09/ex3_renderer.rb: -------------------------------------------------------------------------------- 1 | 2 | class TextObject 3 | attr_reader :text, :size_inches, :color 4 | 5 | def initialize(text, size_inches, color) 6 | @text = text 7 | @size_inches = size_inches 8 | @color = color 9 | end 10 | end 11 | 12 | class Renderer 13 | def render(text_object) 14 | text = text_object.text 15 | size = text_object.size_inches 16 | color = text_object.color 17 | 18 | # render the text ... 19 | 20 | "#{text}/#{size}/#{color}" 21 | end 22 | end 23 | 24 | class BritishTextObject 25 | attr_reader :string, :size_mm, :colour 26 | 27 | # ... 28 | 29 | def initialize(string, size_mm, colour) 30 | @string = string 31 | @size_mm = size_mm 32 | @colour = colour 33 | end 34 | end 35 | 36 | -------------------------------------------------------------------------------- /09/ex3_renderer_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex3_renderer' 7 | 8 | class RendererTest < Test::Unit::TestCase 9 | 10 | def test_text_object 11 | t = TextObject.new( 'hello', 0.25, :blue) 12 | assert_equal 'hello', t.text 13 | assert_equal 0.25, t.size_inches 14 | assert_equal :blue, t.color 15 | end 16 | 17 | def test_british_text_object 18 | t = BritishTextObject.new( 'hello', 6, :blue) 19 | assert_equal 'hello', t.string 20 | assert_equal 6, t.size_mm 21 | assert_equal :blue, t.colour 22 | end 23 | 24 | def test_renderer 25 | t = TextObject.new( 'hello', 0.25, :blue) 26 | r = Renderer.new 27 | 28 | assert_equal 'hello/0.25/blue', r.render(t) 29 | end 30 | 31 | 32 | end 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /09/ex4_bto_adapter.rb: -------------------------------------------------------------------------------- 1 | 2 | class BritishTextObjectAdapter < TextObject 3 | def initialize(bto) 4 | @bto = bto 5 | end 6 | 7 | def text 8 | return @bto.string 9 | end 10 | 11 | def size_inches 12 | return @bto.size_mm / 25.4 13 | end 14 | 15 | def color 16 | return @bto.colour 17 | end 18 | end 19 | 20 | -------------------------------------------------------------------------------- /09/ex4_bto_adapter_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex3_renderer' 7 | require_relative 'ex4_bto_adapter' 8 | 9 | class BTOAdapterTest < Test::Unit::TestCase 10 | 11 | def test_bto_adapter 12 | bto = BritishTextObject.new( 'hello', 25.4, :blue) 13 | adapter = BritishTextObjectAdapter.new(bto) 14 | 15 | assert_equal 'hello', adapter.text 16 | assert_equal 1, adapter.size_inches 17 | assert_equal :blue, adapter.color 18 | end 19 | 20 | def test_renderer 21 | bto = BritishTextObject.new( 'hello', 25.4, :blue) 22 | adapter = BritishTextObjectAdapter.new(bto) 23 | 24 | r = Renderer.new 25 | assert_equal 'hello/1.0/blue', r.render(adapter) 26 | end 27 | 28 | 29 | end 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /09/ex5_change_class.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Make sure the original class is loaded 4 | 5 | require_relative 'british_text_object' 6 | 7 | # Now add some methods to the original class 8 | 9 | class BritishTextObject 10 | def color 11 | return colour 12 | end 13 | 14 | def text 15 | return string 16 | end 17 | 18 | def size_inches 19 | return size_mm / 25.4 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /09/ex5_change_class_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex5_change_class' 7 | 8 | class ModifiedBTOClassTest < Test::Unit::TestCase 9 | 10 | def test_text_object 11 | t = TextObject.new( 'hello', 0.25, :blue) 12 | end 13 | 14 | def test_british_text_object 15 | t = BritishTextObject.new( 'hello', 25.4, :blue) 16 | 17 | assert_equal 'hello', t.string 18 | assert_equal 25.4, t.size_mm 19 | assert_equal :blue, t.colour 20 | 21 | assert_equal 'hello', t.text 22 | assert_equal 1, t.size_inches 23 | assert_equal :blue, t.color 24 | end 25 | 26 | def test_renderer 27 | t = BritishTextObject.new( 'cheers', 25.4, :red) 28 | r = Renderer.new 29 | 30 | assert_equal 'cheers/1.0/red', r.render(t) 31 | end 32 | 33 | 34 | end 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /09/ex6_vandal_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | 8 | # Don't do this! 9 | 10 | begin 11 | class Fixnum 12 | def abs 13 | return 42 14 | end 15 | end 16 | 17 | puts 79.abs 18 | puts -1234.abs 19 | rescue 20 | puts "Something bad happened" 21 | end 22 | } 23 | -------------------------------------------------------------------------------- /09/ex7_change_instance_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex3_renderer' 6 | 7 | example %q{ 8 | 9 | bto = BritishTextObject.new('hello', 50.8, :blue) 10 | 11 | class << bto 12 | def color 13 | colour 14 | end 15 | 16 | def text 17 | string 18 | end 19 | 20 | def size_inches 21 | return size_mm/25.4 22 | end 23 | end 24 | 25 | 26 | puts bto.text 27 | puts bto.color 28 | puts bto.size_inches 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /09/ex7a_change_instance_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex3_renderer' 6 | 7 | example %q{ 8 | 9 | bto = BritishTextObject.new('hello', 50.8, :blue) 10 | 11 | def bto.color 12 | colour 13 | end 14 | 15 | def bto.text 16 | string 17 | end 18 | 19 | # ... 20 | 21 | def bto.size_inches 22 | return size_mm/25.4 23 | end 24 | 25 | puts bto.text 26 | puts bto.color 27 | puts bto.size_inches 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /09/message.encrypted: -------------------------------------------------------------------------------- 1 | 9ES TIE 2 | Io SKL 3 | E o�HE Io -------------------------------------------------------------------------------- /09/message.txt: -------------------------------------------------------------------------------- 1 | The rain in spain 2 | falls mainly on 3 | the plain 4 | -------------------------------------------------------------------------------- /09/new_message: -------------------------------------------------------------------------------- 1 | The rain in spain 2 | falls mainly on 3 | the plain 4 | -------------------------------------------------------------------------------- /09/out: -------------------------------------------------------------------------------- 1 | hello out there -------------------------------------------------------------------------------- /09/out.txt: -------------------------------------------------------------------------------- 1 | <z;-,891y9-z>8/7 -------------------------------------------------------------------------------- /10/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /10/ex10_mm_protection.rb: -------------------------------------------------------------------------------- 1 | require 'etc' 2 | 3 | class AccountProtectionProxy 4 | def initialize(real_account, owner_name) 5 | @subject = real_account 6 | @owner_name = owner_name 7 | end 8 | 9 | 10 | def method_missing(name, *args) 11 | check_access 12 | @subject.send( name, *args ) 13 | end 14 | 15 | def check_access 16 | if Etc.getlogin != @owner_name 17 | raise "Illegal access: #{Etc.getlogin} cannot access account." 18 | end 19 | end 20 | end 21 | 22 | -------------------------------------------------------------------------------- /10/ex10_mm_protection_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require_relative 'ex10_mm_protection' 5 | require_relative 'ex1_account' 6 | 7 | 8 | example %q{ 9 | s = AccountProtectionProxy.new( "a simple string", 'russolsen' ) 10 | puts "The length of the string is #{s.length}" 11 | } 12 | 13 | expect_error %q{ 14 | s = AccountProtectionProxy.new( "a simple string", 'fred' ) 15 | puts "The length of the string is #{s.length}" 16 | } 17 | -------------------------------------------------------------------------------- /10/ex11_mm_virtual.rb: -------------------------------------------------------------------------------- 1 | 2 | class VirtualProxy 3 | def initialize(&creation_block) 4 | @creation_block = creation_block 5 | end 6 | 7 | def method_missing(name, *args) 8 | s = subject 9 | s.send( name, *args) 10 | end 11 | 12 | def subject 13 | @subject = @creation_block.call unless @subject 14 | @subject 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /10/ex11_mm_virtual_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require_relative 'ex11_mm_virtual' 5 | require_relative 'ex1_account' 6 | 7 | 8 | example %q{ 9 | array = VirtualProxy.new { Array.new } 10 | 11 | array << 'hello' 12 | array << 'out' 13 | array << 'there' 14 | } 15 | 16 | -------------------------------------------------------------------------------- /10/ex11_mm_virtual_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_account' 7 | require_relative 'ex11_mm_virtual' 8 | 9 | class MethodMissingVirtualProxyAccountTest < Test::Unit::TestCase 10 | 11 | 12 | def test_proxy 13 | proxy = VirtualProxy.new { BankAccount.new(100) } 14 | assert_nil proxy.instance_variable_get(:@subject) 15 | 16 | assert_equal 100, proxy.balance 17 | assert_not_nil proxy.instance_variable_get(:@subject) 18 | 19 | assert_equal 200, proxy.deposit(100) 20 | assert_equal 200, proxy.balance 21 | 22 | assert_equal 180, proxy.withdraw(20) 23 | assert_equal 180, proxy.balance 24 | assert_not_nil proxy.instance_variable_get(:@subject) 25 | end 26 | 27 | end 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /10/ex12_drb_client_demo_norun.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example %q{ 5 | require_relative 'drb/drb' 6 | 7 | DRb.start_service 8 | math_service = DRbObject.new_with_uri("druby://localhost:3030") 9 | sum=math_service.add(2,2) 10 | puts "The sum is #{sum}." 11 | 12 | } 13 | -------------------------------------------------------------------------------- /10/ex12_drb_server_demo_norun.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | example %q{ 5 | 6 | # 7 | # A service to aid the numerically challenged. 8 | # 9 | class MathService 10 | def add(a, b) 11 | return a + b 12 | end 13 | end 14 | 15 | require_relative 'drb/drb' 16 | 17 | math_service=MathService.new 18 | DRb.start_service("druby://localhost:3030", math_service) 19 | DRb.thread.join 20 | 21 | } 22 | -------------------------------------------------------------------------------- /10/ex13_identity_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require_relative 'ex11_mm_virtual' 5 | require_relative 'ex1_account' 6 | 7 | 8 | example %q{ 9 | account = VirtualProxy.new { BankAccount.new } 10 | 11 | puts account 12 | } 13 | 14 | -------------------------------------------------------------------------------- /10/ex14_speed_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require_relative 'ex11_mm_virtual' 5 | require_relative 'ex1_account' 6 | 7 | 8 | example %q{ 9 | 10 | class DirectCall 11 | def add(a, b) 12 | a+b 13 | end 14 | end 15 | 16 | class MethodMissingCall 17 | def method_missing(name, a, b) 18 | a+b 19 | end 20 | end 21 | 22 | 23 | dc = DirectCall.new 24 | 25 | t0 = Time.new 26 | 100000.times { dc.add(2,2) } 27 | t1 = Time.new 28 | puts "Time for direct call: #{t1-t0}" 29 | 30 | mmc = MethodMissingCall.new 31 | t0 = Time.new 32 | 100000.times { mmc.add(2,2) } 33 | t1 = Time.new 34 | puts "Time for direct call: #{t1-t0}" 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /10/ex1_account.rb: -------------------------------------------------------------------------------- 1 | 2 | class BankAccount 3 | attr_reader :balance 4 | 5 | def initialize(starting_balance=0) 6 | @balance = starting_balance 7 | end 8 | 9 | def deposit(amount) 10 | @balance += amount 11 | end 12 | 13 | def withdraw(amount) 14 | @balance -= amount 15 | end 16 | end 17 | 18 | -------------------------------------------------------------------------------- /10/ex1_account_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_account' 7 | 8 | class AccountTest < Test::Unit::TestCase 9 | 10 | 11 | def test_account 12 | ba = BankAccount.new(100) 13 | assert_equal 100, ba.balance 14 | 15 | assert_equal 200, ba.deposit(100) 16 | assert_equal 200, ba.balance 17 | 18 | assert_equal 180, ba.withdraw(20) 19 | assert_equal 180, ba.balance 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /10/ex2_proxy.rb: -------------------------------------------------------------------------------- 1 | 2 | class BankAccountProxy 3 | def initialize(real_object) 4 | @real_object = real_object 5 | end 6 | 7 | def balance 8 | @real_object.balance 9 | end 10 | 11 | def deposit(amount) 12 | @real_object.deposit(amount) 13 | end 14 | 15 | def withdraw(amount) 16 | @real_object.withdraw(amount) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /10/ex2_proxy_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require_relative 'ex1_account' 5 | require_relative 'ex2_proxy' 6 | 7 | 8 | example %q{ 9 | 10 | 11 | account = BankAccount.new(100) 12 | account.deposit(50) 13 | account.withdraw(10) 14 | 15 | proxy = BankAccountProxy.new(account) 16 | proxy.deposit(50) 17 | proxy.withdraw(10) 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /10/ex2_proxy_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_account' 7 | require_relative 'ex2_proxy' 8 | 9 | class ProxyAccountTest < Test::Unit::TestCase 10 | 11 | 12 | def test_proxy 13 | 14 | ba = BankAccount.new(100) 15 | proxy = BankAccountProxy.new(ba) 16 | 17 | assert_equal 100, ba.balance 18 | assert_equal 100, proxy.balance 19 | 20 | assert_equal 200, proxy.deposit(100) 21 | assert_equal 200, ba.balance 22 | assert_equal 200, proxy.balance 23 | 24 | assert_equal 180, proxy.withdraw(20) 25 | assert_equal 180, ba.balance 26 | assert_equal 180, proxy.balance 27 | end 28 | 29 | end 30 | -------------------------------------------------------------------------------- /10/ex3_protect.rb: -------------------------------------------------------------------------------- 1 | require 'etc' 2 | 3 | class AccountProtectionProxy 4 | def initialize(real_account, owner_name) 5 | @subject = real_account 6 | @owner_name = owner_name 7 | end 8 | 9 | def deposit(amount) 10 | check_access 11 | return @subject.deposit(amount) 12 | end 13 | 14 | def withdraw(amount) 15 | check_access 16 | return @subject.withdraw(amount) 17 | end 18 | 19 | def balance 20 | check_access 21 | return @subject.balance 22 | end 23 | 24 | def check_access 25 | if Etc.getlogin != @owner_name 26 | raise "Illegal access: #{Etc.getlogin} cannot access account." 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /10/ex4_soap_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | puts "This demo does not work in 2013" 5 | 6 | ## require_relative 'yaml' 7 | ## 8 | ## example %q{ 9 | ## require 'soap/wsdlDriver' 10 | ## 11 | ## wsdl_url = 'http://www.webservicex.net/WeatherForecast.asmx?WSDL' 12 | ## 13 | ## proxy = SOAP::WSDLDriverFactory.new( wsdl_url ).create_rpc_driver 14 | ## weather_info = proxy.GetWeatherByZipCode('ZipCode'=>'19128') 15 | ## 16 | ## } 17 | -------------------------------------------------------------------------------- /10/ex5_virtual.rb: -------------------------------------------------------------------------------- 1 | class VirtualAccountProxy 2 | def initialize(starting_balance=0) 3 | @starting_balance=starting_balance 4 | end 5 | 6 | def deposit(amount) 7 | s = subject 8 | return s.deposit(amount) 9 | end 10 | 11 | def withdraw(amount) 12 | s = subject 13 | return s.withdraw(amount) 14 | end 15 | 16 | def balance 17 | s = subject 18 | return s.balance 19 | end 20 | 21 | def subject 22 | @subject || (@subject = BankAccount.new(@starting_balance)) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /10/ex5_virtual_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | require 'yaml' 6 | 7 | require_relative 'ex1_account' 8 | require_relative 'ex5_virtual' 9 | 10 | 11 | class VirtualProxyTest < Test::Unit::TestCase 12 | def test_proxy 13 | proxy = VirtualAccountProxy.new( 100 ) 14 | assert_nil proxy.instance_variable_get(:@subject) 15 | 16 | assert_equal 100, proxy.balance 17 | assert_not_nil proxy.instance_variable_get(:@subject) 18 | 19 | assert_equal 200, proxy.deposit(100) 20 | assert_equal 200, proxy.balance 21 | 22 | assert_equal 180, proxy.withdraw(20) 23 | assert_equal 180, proxy.balance 24 | assert_not_nil proxy.instance_variable_get(:@subject) 25 | end 26 | end 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /10/ex6_virtual_block.rb: -------------------------------------------------------------------------------- 1 | class VirtualAccountProxy 2 | def initialize(&creation_block) 3 | @creation_block = creation_block 4 | end 5 | 6 | # Other methods omitted ... 7 | 8 | def deposit(amount) 9 | s = subject 10 | s.deposit(amount) 11 | end 12 | 13 | def withdraw(amount) 14 | s = subject 15 | s.withdraw(amount) 16 | end 17 | 18 | def balance 19 | s = subject 20 | s.balance 21 | end 22 | 23 | def subject 24 | @subject || (@subject = @creation_block.call) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /10/ex6_virtual_block_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require_relative 'ex6_virtual_block' 5 | require_relative 'ex1_account' 6 | 7 | example %q{ 8 | 9 | account = VirtualAccountProxy.new { BankAccount.new(10) } 10 | 11 | pp account 12 | account.deposit(1) 13 | pp account 14 | } 15 | -------------------------------------------------------------------------------- /10/ex6_virtual_block_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | require 'yaml' 6 | 7 | require_relative 'ex1_account' 8 | require_relative 'ex6_virtual_block' 9 | 10 | 11 | class VirtualProxyTest < Test::Unit::TestCase 12 | 13 | def test_proxy 14 | ba = nil 15 | proxy = VirtualAccountProxy.new {ba = BankAccount.new(100)} 16 | 17 | assert_nil ba 18 | 19 | assert_equal 100, proxy.balance 20 | assert_not_nil ba 21 | 22 | assert_equal 200, proxy.deposit(100) 23 | assert_equal 200, proxy.balance 24 | 25 | assert_equal 180, proxy.withdraw(20) 26 | assert_equal 180, proxy.balance 27 | assert_not_nil ba 28 | end 29 | 30 | end 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /10/ex7_meth_missing_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | class TestMethodMissing 8 | def hello 9 | puts "Hello from a real method" 10 | end 11 | 12 | def method_missing(name, *args) 13 | puts "Warning, warning, unknown method called: #{name}" 14 | puts "Arguments: #{args.join(' ')}" 15 | end 16 | end 17 | 18 | 19 | tmm=TestMethodMissing.new 20 | tmm.hello 21 | tmm.hi(1, 2, 3) 22 | tmm.goodbye('cruel', 'world') 23 | } 24 | 25 | example %q{ 26 | tmm=TestMethodMissing.new 27 | 28 | 29 | tmm.send(:hello) 30 | tmm.send(:goodbye, 'cruel', 'world') 31 | } 32 | -------------------------------------------------------------------------------- /10/ex8_almost_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex1_account' 5 | 6 | example %q{ 7 | 8 | class AccountProxy 9 | def initialize(real_account) 10 | @subject = real_account 11 | end 12 | 13 | def method_missing(name, *args) 14 | puts "Drats! There is no #{name} method." 15 | end 16 | end 17 | 18 | ap = AccountProxy.new( BankAccount.new(100) ) 19 | ap.deposit(25) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /10/ex9_meth_miss_proxy.rb: -------------------------------------------------------------------------------- 1 | 2 | class AccountProxy 3 | def initialize(real_account) 4 | @subject = real_account 5 | end 6 | 7 | def method_missing(name, *args) 8 | puts "Delegating #{name} message to subject." 9 | @subject.send(name, *args) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /10/ex9_meth_miss_proxy_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require_relative 'ex9_meth_miss_proxy' 5 | require_relative 'ex1_account' 6 | 7 | 8 | example %q{ 9 | 10 | ap = AccountProxy.new( BankAccount.new(100) ) 11 | ap.deposit(25) 12 | ap.withdraw(50) 13 | puts "account balance is now: #{ap.balance}" 14 | 15 | } 16 | -------------------------------------------------------------------------------- /10/ex9_meth_miss_proxy_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_account' 7 | require_relative 'ex9_meth_miss_proxy' 8 | 9 | class MethodMissingProxyAccountTest < Test::Unit::TestCase 10 | 11 | def test_proxy 12 | 13 | ba = BankAccount.new(100) 14 | proxy = AccountProxy.new(ba) 15 | 16 | assert_equal 100, ba.balance 17 | assert_equal 100, proxy.balance 18 | 19 | assert_equal 200, proxy.deposit(100) 20 | assert_equal 200, ba.balance 21 | assert_equal 200, proxy.balance 22 | 23 | assert_equal 180, proxy.withdraw(20) 24 | assert_equal 180, ba.balance 25 | assert_equal 180, proxy.balance 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /11/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | 3 | task :clean do 4 | rm_f 'final.txt' 5 | rm_f 'out.txt' 6 | rm_f 'out' 7 | end 8 | 9 | -------------------------------------------------------------------------------- /11/ex1_enhanced_writer.rb: -------------------------------------------------------------------------------- 1 | class EnhancedWriter 2 | attr_reader :check_sum 3 | 4 | def initialize(path) 5 | @file = File.open(path, "w") 6 | @check_sum = 0 7 | @line_number = 1 8 | end 9 | 10 | def write_line(line) 11 | @file.print(line) 12 | @file.print("\n") 13 | end 14 | 15 | def checksumming_write_line(data) 16 | data.each_byte {|byte| @check_sum = (@check_sum + byte.ord) % 256 } 17 | @check_sum += "\n".ord % 256 18 | write_line(data) 19 | end 20 | 21 | def timestamping_write_line(data) 22 | write_line("#{Time.new}: #{data}") 23 | end 24 | 25 | def numbering_write_line(data) 26 | write_line("%{@line_number}: #{data}") 27 | @line_number += 1 28 | end 29 | 30 | def close 31 | @file.close 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /11/ex1_enhanced_writer_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex1_enhanced_writer' 5 | 6 | example %q{ 7 | writer = EnhancedWriter.new('out.txt') 8 | writer.write_line('A plain line') 9 | 10 | writer.checksumming_write_line('A line with check sum') 11 | puts("Check sum is #{writer.check_sum}") 12 | 13 | writer.timestamping_write_line('with time stamp') 14 | writer.numbering_write_line('with line number') 15 | 16 | writer.close 17 | } 18 | -------------------------------------------------------------------------------- /11/ex2_decorator.rb: -------------------------------------------------------------------------------- 1 | class WriterDecorator 2 | def initialize(real_writer) 3 | @real_writer = real_writer 4 | end 5 | 6 | def write_line(line) 7 | @real_writer.write_line(line) 8 | end 9 | 10 | def pos 11 | @real_writer.pos 12 | end 13 | 14 | def rewind 15 | @real_writer.rewind 16 | end 17 | 18 | def close 19 | @real_writer.close 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /11/ex2_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex2_simple_writer' 6 | require_relative 'ex2_decorator' 7 | require_relative 'ex2_misc_decorators' 8 | 9 | example %q{ 10 | writer = NumberingWriter.new(SimpleWriter.new('final.txt')) 11 | 12 | writer.write_line('Hello out there') 13 | } 14 | 15 | example %q{ 16 | writer = TimeStampingWriter.new(NumberingWriter.new( 17 | CheckSummingWriter.new(SimpleWriter.new('final.txt')))) 18 | 19 | writer.write_line('Hello out there') 20 | 21 | 22 | pp writer 23 | } 24 | -------------------------------------------------------------------------------- /11/ex2_simple_writer.rb: -------------------------------------------------------------------------------- 1 | class SimpleWriter 2 | def initialize(path) 3 | @file = File.open(path, 'w') 4 | end 5 | 6 | def write_line(line) 7 | @file.print(line) 8 | @file.print("\n") 9 | end 10 | 11 | def pos 12 | @file.pos 13 | end 14 | 15 | def rewind 16 | @file.rewind 17 | end 18 | 19 | def close 20 | @file.close 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /11/ex3_forwardable.rb: -------------------------------------------------------------------------------- 1 | require 'forwardable' 2 | 3 | class WriterDecorator 4 | extend Forwardable 5 | 6 | def_delegators :@real_writer, :write_line, :rewind, :pos, :close 7 | 8 | def initialize(real_writer) 9 | @real_writer = real_writer 10 | end 11 | 12 | end 13 | 14 | -------------------------------------------------------------------------------- /11/ex3_forwardable_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex2_simple_writer' 7 | require_relative 'ex3_forwardable' 8 | require_relative 'ex2_misc_decorators' 9 | 10 | 11 | class DecoratedWriterTest < Test::Unit::TestCase 12 | def setup 13 | File.delete('out') if File.exists?('out') 14 | end 15 | 16 | def test_decorator 17 | w = SimpleWriter.new('out') 18 | assert_simple_writer_behavior(WriterDecorator.new(w)) 19 | end 20 | 21 | def assert_simple_writer_behavior(w) 22 | assert_equal 0, w.pos 23 | w.write_line("a") 24 | assert_equal 2, w.pos 25 | w.write_line("b") 26 | assert_equal 4, w.pos 27 | w.rewind 28 | assert_equal 0, w.pos 29 | w.write_line("X") 30 | w.write_line("Y") 31 | w.close 32 | assert_equal "X\nY\n", File.read('out') 33 | end 34 | end 35 | 36 | -------------------------------------------------------------------------------- /11/ex4_wrap_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex2_simple_writer' 6 | 7 | 8 | example %q{ 9 | w = SimpleWriter.new('out') 10 | 11 | class << w 12 | 13 | alias old_write_line write_line 14 | 15 | def write_line(line) 16 | old_write_line("#{Time.new}: #{line}") 17 | end 18 | 19 | end 20 | 21 | 22 | w.write_line("hello with a timestamp") 23 | 24 | } 25 | -------------------------------------------------------------------------------- /11/ex5_module.rb: -------------------------------------------------------------------------------- 1 | 2 | module TimeStampingWriter 3 | def write_line(line) 4 | super("#{Time.new}: #{line}") 5 | end 6 | end 7 | 8 | module NumberingWriter 9 | attr_reader :line_number 10 | 11 | def write_line(line) 12 | @line_number = 1 unless @line_number 13 | super("#{@line_number}: #{line}") 14 | @line_number += 1 15 | end 16 | end 17 | 18 | -------------------------------------------------------------------------------- /11/ex5_module_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex2_simple_writer' 7 | require_relative 'ex5_module' 8 | 9 | class ModuleDecoratorTest < Test::Unit::TestCase 10 | 11 | def setup 12 | File.delete('out') if File.exists?('out') 13 | end 14 | 15 | def test_simple_writer 16 | w = SimpleWriter.new('out') 17 | w.extend(NumberingWriter) 18 | w.extend(TimeStampingWriter) 19 | 20 | w.write_line('hello') 21 | w.close 22 | 23 | assert /1: .*: hello\n/ =~ File.read('out') 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /11/ex6_alias_method_chain_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require 'rubygems' 6 | require 'facets' 7 | 8 | 9 | example %q{ 10 | 11 | class Writer 12 | def write_line(line) 13 | puts(line) 14 | end 15 | 16 | def write_line_with_numbering(line) 17 | @number = 1 unless @number 18 | write_line_without_numbering("#{@number}: #{line}") 19 | @number += 1 20 | end 21 | 22 | def write_line_with_timestamp(line) 23 | write_line_without_timestamp("#{Time.new}: #{line}") 24 | end 25 | 26 | alias_method_chain :write_line, :numbering 27 | alias_method_chain :write_line, :timestamp 28 | end 29 | 30 | w=Writer.new 31 | w.write_line('hello world') 32 | 33 | } 34 | -------------------------------------------------------------------------------- /12/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /12/ex10_subvert_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | require 'singleton' 7 | 8 | class Manager 9 | include Singleton 10 | 11 | def manage_resources 12 | puts("I am managing my resources") 13 | end 14 | end 15 | 16 | begin 17 | m = Manager.new 18 | rescue NoMethodError => e 19 | puts e 20 | end 21 | 22 | 23 | class Manager 24 | public_class_method :new 25 | end 26 | 27 | 28 | m = Manager.new 29 | puts "new manager: #{m}" 30 | } 31 | 32 | -------------------------------------------------------------------------------- /12/ex10a_subvert_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex8_class_singleton' 6 | 7 | example %q{ 8 | 9 | a_second_logger = ClassBasedLogger.clone 10 | a_second_logger.error('using a second logger') 11 | 12 | puts ClassBasedLogger 13 | puts a_second_logger 14 | 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /12/ex11_thread_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | 6 | example %q{ 7 | 8 | thread1 = Thread.new do 9 | sum=0 10 | 1.upto(10) {|x| sum = sum + x} 11 | puts "The sum of the first 10 integers is #{sum}" 12 | end 13 | 14 | thread2 = Thread.new do 15 | product=1 16 | 1.upto(10) {|x| product = product * x} 17 | puts "The product of the first 10 integers is #{product}" 18 | end 19 | 20 | thread1.join 21 | thread2.join 22 | } 23 | -------------------------------------------------------------------------------- /12/ex12_monitor_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | require 'monitor' 8 | 9 | 10 | class Account 11 | 12 | attr_reader :balance 13 | 14 | def initialize 15 | @balance=0 16 | @monitor = Monitor.new 17 | end 18 | 19 | def transaction(amount) 20 | @monitor.synchronize do 21 | # Only one thread at a time here 22 | @balance += amount 23 | end 24 | end 25 | 26 | end 27 | 28 | account = Account.new 29 | 30 | t2=Thread.new do 31 | 100000.times { account.transaction(-10)} 32 | end 33 | 34 | t1=Thread.new do 35 | 100000.times { account.transaction(10) } 36 | end 37 | 38 | t1.join 39 | t2.join 40 | puts account.balance 41 | } 42 | -------------------------------------------------------------------------------- /12/ex15_subclass.rb: -------------------------------------------------------------------------------- 1 | require 'singleton' 2 | 3 | class SimpleLogger 4 | # All of the logging functionality in this class.. 5 | end 6 | 7 | 8 | class SingletonLogger < SimpleLogger 9 | include Singleton 10 | end 11 | -------------------------------------------------------------------------------- /12/ex15_subclass_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex15_subclass' 7 | 8 | class CureTestingBluesTest < Test::Unit::TestCase 9 | 10 | def test_private_new 11 | assert_raise(NoMethodError){ SingletonLogger.new } 12 | end 13 | 14 | def test_public_new 15 | logger = SimpleLogger.new 16 | end 17 | 18 | def test_class_relationship 19 | assert SingletonLogger.instance.kind_of?(SimpleLogger) 20 | end 21 | 22 | end 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /12/ex1_class_attr_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require 'pp' 5 | 6 | example %q{ 7 | 8 | class ClassVariableTester 9 | @@class_count = 0 10 | 11 | def initialize 12 | @instance_count = 0 13 | end 14 | 15 | def increment 16 | @@class_count = @@class_count + 1 17 | @instance_count = @instance_count + 1 18 | end 19 | 20 | def to_s 21 | "class_count: #{@@class_count} instance_count: #{@instance_count}" 22 | end 23 | end 24 | 25 | c1 = ClassVariableTester.new 26 | c1.increment 27 | c1.increment 28 | puts("c1: #{c1}") 29 | 30 | c2 = ClassVariableTester.new 31 | puts("c2: #{c2}") 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /12/ex2_class_method_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | expect_error %q{ 6 | 7 | class SomeClass 8 | def a_method 9 | puts('hello from a method') 10 | end 11 | end 12 | 13 | SomeClass.a_method 14 | } 15 | 16 | example %q{ 17 | class SomeClass 18 | puts("Inside a class def, self is #{self}") 19 | end 20 | } 21 | 22 | example %q{ 23 | 24 | class SomeClass 25 | def self.class_level_method 26 | puts('hello from the class method') 27 | end 28 | end 29 | 30 | SomeClass.class_level_method 31 | } 32 | 33 | example %q{ 34 | 35 | class SomeClass 36 | def SomeClass.class_level_method 37 | puts('hello from the class method') 38 | end 39 | end 40 | 41 | SomeClass.class_level_method 42 | } 43 | 44 | -------------------------------------------------------------------------------- /12/ex3_simple_logger.rb: -------------------------------------------------------------------------------- 1 | class SimpleLogger 2 | attr_accessor :level 3 | 4 | ERROR = 1 5 | WARNING = 2 6 | INFO = 3 7 | 8 | def initialize 9 | @log = File.open("log.txt", "w") 10 | @level = WARNING 11 | end 12 | 13 | def error(msg) 14 | @log.puts(msg) 15 | @log.flush 16 | end 17 | 18 | def warning(msg) 19 | @log.puts(msg) if @level >= WARNING 20 | @log.flush 21 | end 22 | 23 | def info(msg) 24 | @log.puts(msg) if @level >= INFO 25 | @log.flush 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /12/ex3_simple_logger_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex3_simple_logger' 6 | 7 | 8 | example %q{ 9 | 10 | logger = SimpleLogger.new 11 | logger.level = SimpleLogger::INFO 12 | 13 | logger.info('Doing the first thing') 14 | # Do the first thing... 15 | logger.info('Now doing the second thing') 16 | # Do the second thing... 17 | 18 | } 19 | -------------------------------------------------------------------------------- /12/ex4_manual.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class SimpleLogger 4 | 5 | # Lots of code deleted ... 6 | 7 | @@instance = SimpleLogger.new 8 | 9 | def self.instance 10 | return @@instance 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /12/ex4_manual_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex3_simple_logger' 6 | require_relative 'ex4_manual' 7 | 8 | example %q{ 9 | 10 | logger1 = SimpleLogger.instance # Returns the logger 11 | logger2 = SimpleLogger.instance # Returns exactly the same logger 12 | 13 | 14 | SimpleLogger.instance.info('Computer wins chess game.') 15 | SimpleLogger.instance.warning('AE-35 hardware failure predicted.') 16 | SimpleLogger.instance.error( 17 | 'HAL-9000 malfunction, take emergency action!') 18 | 19 | } 20 | -------------------------------------------------------------------------------- /12/ex4_manual_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex3_simple_logger' 7 | require_relative 'ex4_manual' 8 | 9 | class ManualSingletonTest < Test::Unit::TestCase 10 | 11 | def test_singleton 12 | logger1 = SimpleLogger.instance 13 | logger2 = SimpleLogger.instance 14 | assert_equal logger1, logger2 15 | assert_equal SimpleLogger, logger1.class 16 | end 17 | 18 | end 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /12/ex5_private.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class SimpleLogger 4 | 5 | # Lots of code deleted ... 6 | 7 | @@instance = SimpleLogger.new 8 | 9 | def self.instance 10 | return @@instance 11 | end 12 | 13 | private_class_method :new 14 | end 15 | -------------------------------------------------------------------------------- /12/ex5_private_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex3_simple_logger' 7 | require_relative 'ex5_private' 8 | 9 | class PrivateNewSingletonTest < Test::Unit::TestCase 10 | 11 | def test_private_new 12 | assert_raise(NoMethodError){ SimpleLogger.new } 13 | end 14 | 15 | end 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /12/ex6_std_singleton.rb: -------------------------------------------------------------------------------- 1 | require 'singleton' 2 | 3 | class SimpleLogger 4 | include Singleton 5 | 6 | # Lots of code deleted... 7 | 8 | end 9 | -------------------------------------------------------------------------------- /12/ex6_std_singleton_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex3_simple_logger' 7 | require_relative 'ex6_std_singleton' 8 | 9 | class StandardSingletonTest < Test::Unit::TestCase 10 | 11 | def test_singleton 12 | logger1 = SimpleLogger.instance 13 | logger2 = SimpleLogger.instance 14 | assert_equal logger1, logger2 15 | assert_equal SimpleLogger, logger1.class 16 | end 17 | 18 | end 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /12/ex7_global_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex3_simple_logger' 6 | 7 | 8 | class LoggerThatDoesSomethingBad 9 | end 10 | 11 | example %q{ 12 | 13 | $logger = SimpleLogger.new 14 | 15 | $logger = LoggerThatDoesSomethingBad.new 16 | 17 | } 18 | 19 | example %q{ 20 | Logger = SimpleLogger.new 21 | 22 | Logger = LoggerThatDoesSomethingBad.new 23 | } 24 | 25 | -------------------------------------------------------------------------------- /12/ex8_class_singleton.rb: -------------------------------------------------------------------------------- 1 | 2 | class ClassBasedLogger 3 | ERROR = 1 4 | WARNING = 2 5 | INFO = 3 6 | 7 | @log = File.open('log.txt', 'w') 8 | @level = WARNING 9 | 10 | def self.error(msg) 11 | @log.puts(msg) 12 | @log.flush 13 | end 14 | 15 | def self.warning(msg) 16 | @log.puts(msg) if @level >= WARNING 17 | @log.flush 18 | end 19 | 20 | def self.info(msg) 21 | @log.puts(msg) if @level >= INFO 22 | @log.flush 23 | end 24 | 25 | def self.level=(new_level) 26 | @level = new_level 27 | end 28 | 29 | def self.level 30 | @level 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /12/ex8_class_singleton_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex8_class_singleton' 6 | 7 | 8 | example %q{ 9 | 10 | ClassBasedLogger.level = ClassBasedLogger::INFO 11 | 12 | ClassBasedLogger.info('Computer wins chess game.') 13 | ClassBasedLogger.warning('AE-35 hardware failure predicted.') 14 | ClassBasedLogger.error('HAL-9000 malfunction, take emergency action!') 15 | 16 | } 17 | -------------------------------------------------------------------------------- /12/ex9_module_singleton.rb: -------------------------------------------------------------------------------- 1 | 2 | module ModuleBasedLogger 3 | ERROR = 1 4 | WARNING = 2 5 | INFO = 3 6 | 7 | @@log = File.open("log.txt", "w") 8 | @@level = WARNING 9 | 10 | def self.error(msg) 11 | @@log.puts(msg) 12 | @@log.flush 13 | end 14 | 15 | 16 | # Logs of code, exactly like the 17 | # ClassBasedSingleton deleted... 18 | 19 | 20 | 21 | 22 | 23 | def self.warning(msg) 24 | @@log.puts(msg) if @@level >= WARNING 25 | @@log.flush 26 | end 27 | 28 | def self.info(msg) 29 | @@log.puts(msg) if @@level >= INFO 30 | @@log.flush 31 | end 32 | 33 | def self.level=(new_level) 34 | @@level = new_level 35 | end 36 | 37 | def self.level 38 | @@level 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /12/ex9_module_singleton_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex9_module_singleton' 6 | 7 | 8 | example %q{ 9 | 10 | ModuleBasedLogger.info('Computer wins chess game.') 11 | 12 | } 13 | -------------------------------------------------------------------------------- /12/log.txt: -------------------------------------------------------------------------------- 1 | yes 2 | yes 3 | yes 4 | yes 5 | yes 6 | yes 7 | -------------------------------------------------------------------------------- /13/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /13/ex10_habitat.rb: -------------------------------------------------------------------------------- 1 | 2 | class Habitat 3 | def initialize(number_animals, animal_class, 4 | number_plants, plant_class) 5 | @animal_class = animal_class 6 | @plant_class = plant_class 7 | 8 | @animals = [] 9 | number_animals.times do |i| 10 | animal = new_organism(:animal, "Animal#{i}") 11 | @animals << animal 12 | end 13 | 14 | @plants = [] 15 | number_plants.times do |i| 16 | plant = new_organism(:plant, "Plant#{i}") 17 | @plants << plant 18 | end 19 | end 20 | 21 | def simulate_one_day 22 | @plants.each {|plant| plant.grow} 23 | @animals.each {|animal| animal.speak} 24 | @animals.each {|animal| animal.eat} 25 | @animals.each {|animal| animal.sleep} 26 | end 27 | 28 | def new_organism(type, name) 29 | if type == :animal 30 | @animal_class.new(name) 31 | elsif type == :plant 32 | @plant_class.new(name) 33 | else 34 | raise "Unknown organism type: #{type}" 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /13/ex10_habitat_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex1_duck' 5 | require_relative 'ex3_frog' 6 | require_relative 'ex5_plants' 7 | require_relative 'ex9_tiger_tree' 8 | require_relative 'ex10_habitat' 9 | 10 | example %q{ 11 | jungle = Habitat.new(1, Tiger, 4, Tree) 12 | jungle.simulate_one_day 13 | 14 | pond = Habitat.new( 2, Duck, 4, WaterLily) 15 | pond.simulate_one_day 16 | } 17 | 18 | example %q{ 19 | unstable = Habitat.new( 2, Tiger, 4, WaterLily) 20 | unstable.simulate_one_day 21 | } 22 | -------------------------------------------------------------------------------- /13/ex10_habitat_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_duck' 7 | require_relative 'ex9_tiger_tree' 8 | require_relative 'ex10_habitat' 9 | 10 | class HabitatTest < Test::Unit::TestCase 11 | 12 | def test_habitat 13 | p = Habitat.new(3, Tiger, 4, Tree) 14 | assert_equal Array, p.instance_variable_get(:@animals).class 15 | assert_equal Tiger, p.instance_variable_get(:@animals)[0].class 16 | assert_equal 3, p.instance_variable_get(:@animals).size 17 | 18 | assert_equal Array, p.instance_variable_get(:@plants).class 19 | assert_equal Tree, p.instance_variable_get(:@plants)[0].class 20 | assert_equal 4, p.instance_variable_get(:@plants).size 21 | p.simulate_one_day 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /13/ex11_abs_factories.rb: -------------------------------------------------------------------------------- 1 | 2 | class PondOrganismFactory 3 | def new_animal(name) 4 | Frog.new(name) 5 | end 6 | 7 | def new_plant(name) 8 | Algae.new(name) 9 | end 10 | end 11 | 12 | class JungleOrganismFactory 13 | def new_animal(name) 14 | Tiger.new(name) 15 | end 16 | 17 | def new_plant(name) 18 | Tree.new(name) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /13/ex11_abs_factories_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_duck' 7 | require_relative 'ex3_frog' 8 | require_relative 'ex5_plants' 9 | require_relative 'ex9_tiger_tree' 10 | require_relative 'ex11_abs_factories' 11 | 12 | class AbsFactoryTest < Test::Unit::TestCase 13 | 14 | def test_pond_factory 15 | pf = PondOrganismFactory.new 16 | assert_equal Frog, pf.new_animal('').class 17 | assert_equal Algae, pf.new_plant('').class 18 | end 19 | 20 | def test_jungle_factory 21 | pf = JungleOrganismFactory.new 22 | assert_equal Tiger, pf.new_animal('').class 23 | assert_equal Tree, pf.new_plant('').class 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /13/ex12_abs_fac_habitat.rb: -------------------------------------------------------------------------------- 1 | 2 | class Habitat 3 | def initialize(number_animals, number_plants, organism_factory) 4 | @organism_factory = organism_factory 5 | 6 | @animals = [] 7 | number_animals.times do |i| 8 | animal = @organism_factory.new_animal("Animal#{i}") 9 | @animals << animal 10 | end 11 | 12 | @plants = [] 13 | number_plants.times do |i| 14 | plant = @organism_factory.new_plant("Plant#{i}") 15 | @plants << plant 16 | end 17 | end 18 | 19 | # Rest of the class ... 20 | 21 | 22 | 23 | def simulate_one_day 24 | @plants.each {|plant| plant.grow} 25 | @animals.each {|animal| animal.speak} 26 | @animals.each {|animal| animal.eat} 27 | @animals.each {|animal| animal.sleep} 28 | end 29 | 30 | def new_organism(type, name) 31 | if type == :animal 32 | @animal_class.new(name) 33 | elsif type == :plant 34 | @plant_class.new(name) 35 | else 36 | raise "Unknown organism type: #{type}" 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /13/ex12_abs_fac_habitat_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex1_duck' 5 | require_relative 'ex3_frog' 6 | require_relative 'ex5_plants' 7 | require_relative 'ex9_tiger_tree' 8 | require_relative 'ex11_abs_factories' 9 | require_relative 'ex12_abs_fac_habitat' 10 | 11 | example %q{ 12 | jungle = Habitat.new(1, 4, JungleOrganismFactory.new) 13 | jungle.simulate_one_day 14 | 15 | pond = Habitat.new( 2, 4, PondOrganismFactory.new) 16 | pond.simulate_one_day 17 | } 18 | 19 | -------------------------------------------------------------------------------- /13/ex12_abs_fac_habitat_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_duck' 7 | require_relative 'ex3_frog' 8 | require_relative 'ex5_plants' 9 | require_relative 'ex9_tiger_tree' 10 | require_relative 'ex11_abs_factories' 11 | require_relative 'ex12_abs_fac_habitat' 12 | 13 | class AbsFactoryHabitatTest < Test::Unit::TestCase 14 | 15 | def test_pond_habitat 16 | pf = PondOrganismFactory.new 17 | h = Habitat.new( 1, 1, pf) 18 | assert_equal Frog, h.instance_variable_get(:@animals)[0].class 19 | assert_equal Algae, h.instance_variable_get(:@plants)[0].class 20 | h.simulate_one_day 21 | end 22 | 23 | def test_jungle_habitat 24 | jf = JungleOrganismFactory.new 25 | h = Habitat.new( 1, 1, jf) 26 | assert_equal Tiger, h.instance_variable_get(:@animals)[0].class 27 | assert_equal Tree, h.instance_variable_get(:@plants)[0].class 28 | h.simulate_one_day 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /13/ex13_abs_fac_with_classes.rb: -------------------------------------------------------------------------------- 1 | class OrganismFactory 2 | def initialize(plant_class, animal_class) 3 | @plant_class = plant_class 4 | @animal_class = animal_class 5 | end 6 | 7 | def new_animal(name) 8 | @animal_class.new(name) 9 | end 10 | 11 | def new_plant(name) 12 | @plant_class.new(name) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /13/ex13_abs_fac_with_classes_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex1_duck' 5 | require_relative 'ex3_frog' 6 | require_relative 'ex5_plants' 7 | require_relative 'ex9_tiger_tree' 8 | require_relative 'ex12_abs_fac_habitat' 9 | require_relative 'ex13_abs_fac_with_classes' 10 | 11 | example %q{ 12 | jungle_organism_factory = OrganismFactory.new(Tree, Tiger) 13 | pond_organism_factory = OrganismFactory.new(WaterLily, Frog) 14 | 15 | jungle = Habitat.new(1, 4, jungle_organism_factory) 16 | jungle.simulate_one_day 17 | 18 | pond = Habitat.new( 2, 4, pond_organism_factory) 19 | pond.simulate_one_day 20 | } 21 | 22 | -------------------------------------------------------------------------------- /13/ex13_abs_fac_with_classes_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_duck' 7 | require_relative 'ex3_frog' 8 | require_relative 'ex5_plants' 9 | require_relative 'ex9_tiger_tree' 10 | require_relative 'ex13_abs_fac_with_classes' 11 | 12 | class ClassBasedOrganismFactoryTest < Test::Unit::TestCase 13 | 14 | def test_pond_habitat 15 | f = OrganismFactory.new(Tree, Frog) 16 | assert_equal Frog, f.new_animal('').class 17 | assert_equal Tree, f.new_plant('').class 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /13/ex14_names.rb: -------------------------------------------------------------------------------- 1 | 2 | class PDFReader 3 | end 4 | 5 | class HTMLReader 6 | end 7 | 8 | class RTFReader 9 | end 10 | 11 | class PDFWriter 12 | end 13 | 14 | class HTMLWriter 15 | end 16 | 17 | class RTFWriter 18 | end 19 | 20 | class IOFactory 21 | def initialize(format) 22 | @reader_class = eval("#{format}Reader") 23 | @writer_class = eval("#{format}Writer") 24 | end 25 | 26 | def new_reader 27 | @reader_class.new 28 | end 29 | 30 | def new_writer 31 | @writer_class.new 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /13/ex14_names_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex14_names' 5 | 6 | example %q{ 7 | 8 | html_factory = IOFactory.new('HTML') 9 | html_reader = html_factory.new_reader 10 | 11 | pdf_factory = IOFactory.new('PDF') 12 | pdf_writer = pdf_factory.new_writer 13 | } 14 | 15 | -------------------------------------------------------------------------------- /13/ex14_names_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex14_names' 7 | 8 | class NameBasedFactoryTest < Test::Unit::TestCase 9 | 10 | def test_name_based_factory 11 | f = IOFactory.new('HTML') 12 | assert_equal HTMLReader, f.new_reader.class 13 | assert_equal HTMLWriter, f.new_writer.class 14 | 15 | f = IOFactory.new('PDF') 16 | assert_equal PDFReader, f.new_reader.class 17 | assert_equal PDFWriter, f.new_writer.class 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /13/ex15_activerecord_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | require_relative '../example' 4 | 5 | example %q{ 6 | 7 | class Base 8 | # Lots of non adapter related code removed... 9 | end 10 | 11 | 12 | # MySQL adapter 13 | 14 | class Base 15 | def self.mysql_connection(config) 16 | # Create and return a new MySQL connection, using 17 | # the user name, password, etc stored in the 18 | # config hash... 19 | 20 | 21 | puts('mysql connection') 22 | 23 | end 24 | end 25 | 26 | # Oracle adapter 27 | 28 | class Base 29 | def self.oracle_connection(config) 30 | # Create a new Oracle connection ... 31 | 32 | 33 | puts('oracle connection') 34 | 35 | end 36 | end 37 | 38 | config = { :adapter => 'oracle' } 39 | 40 | adapter = "mysql" 41 | method_name = "#{adapter}_connection" 42 | Base.send(method_name, config) 43 | } 44 | -------------------------------------------------------------------------------- /13/ex1_duck.rb: -------------------------------------------------------------------------------- 1 | class Duck 2 | def initialize(name) 3 | @name = name 4 | end 5 | 6 | def eat 7 | puts("Duck #{@name} is eating.") 8 | end 9 | 10 | def speak 11 | puts("Duck #{@name} says Quack!") 12 | end 13 | 14 | def sleep 15 | puts("Duck #{@name} sleeps quietly.") 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /13/ex1_duck_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_duck' 7 | 8 | class DuckTest < Test::Unit::TestCase 9 | 10 | 11 | def test_duck 12 | d = Duck.new('fred') 13 | assert_equal 'fred', d.instance_variable_get(:@name) 14 | 15 | d.eat 16 | d.speak 17 | d.sleep 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /13/ex2_pond.rb: -------------------------------------------------------------------------------- 1 | class Pond 2 | def initialize(number_ducks) 3 | @ducks = [] 4 | number_ducks.times do |i| 5 | duck = Duck.new("Duck#{i}") 6 | @ducks << duck 7 | end 8 | end 9 | 10 | def simulate_one_day 11 | @ducks.each {|duck| duck.speak} 12 | @ducks.each {|duck| duck.eat} 13 | @ducks.each {|duck| duck.sleep} 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /13/ex2_pond_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex1_duck' 5 | require_relative 'ex2_pond' 6 | 7 | example %q{ 8 | pond = Pond.new(3) 9 | pond.simulate_one_day 10 | } 11 | 12 | -------------------------------------------------------------------------------- /13/ex2_pond_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_duck' 7 | require_relative 'ex2_pond' 8 | 9 | class PondTest < Test::Unit::TestCase 10 | 11 | 12 | def test_pond 13 | p = Pond.new(4) 14 | assert_equal Array, p.instance_variable_get(:@ducks).class 15 | assert_equal 4, p.instance_variable_get(:@ducks).size 16 | p.simulate_one_day 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /13/ex3_frog.rb: -------------------------------------------------------------------------------- 1 | class Frog 2 | def initialize(name) 3 | @name = name 4 | end 5 | 6 | def eat 7 | puts("Frog #{@name} is eating.") 8 | end 9 | 10 | def speak 11 | puts("Frog #{@name} says Crooooaaaak!") 12 | end 13 | 14 | def sleep 15 | puts("Frog #{@name} doesn't sleep, he croaks all night!") 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /13/ex3_frog_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex3_frog' 7 | 8 | class FrogTest < Test::Unit::TestCase 9 | def test_frog 10 | d = Frog.new('fred') 11 | assert_equal 'fred', d.instance_variable_get(:@name) 12 | 13 | d.eat 14 | d.speak 15 | d.sleep 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /13/ex4_factory_method.rb: -------------------------------------------------------------------------------- 1 | class Pond 2 | def initialize(number_animals) 3 | @animals = [] 4 | number_animals.times do |i| 5 | animal = new_animal("Animal#{i}") 6 | @animals << animal 7 | end 8 | end 9 | 10 | def simulate_one_day 11 | @animals.each {|animal| animal.speak} 12 | @animals.each {|animal| animal.eat} 13 | @animals.each {|animal| animal.sleep} 14 | end 15 | end 16 | 17 | class DuckPond < Pond 18 | def new_animal(name) 19 | Duck.new(name) 20 | end 21 | end 22 | 23 | class FrogPond < Pond 24 | def new_animal(name) 25 | Frog.new(name) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /13/ex4_factory_method_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex1_duck' 5 | require_relative 'ex3_frog' 6 | require_relative 'ex4_factory_method' 7 | 8 | example %q{ 9 | pond = FrogPond.new(3) 10 | pond.simulate_one_day 11 | } 12 | 13 | -------------------------------------------------------------------------------- /13/ex4_factory_method_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_duck' 7 | require_relative 'ex3_frog' 8 | require_relative 'ex4_factory_method' 9 | 10 | class FactoryMethodPondTest < Test::Unit::TestCase 11 | 12 | 13 | def test_duck_pond 14 | p = DuckPond.new(1) 15 | assert_equal Duck, p.instance_variable_get(:@animals)[0].class 16 | p.simulate_one_day 17 | end 18 | 19 | def test_frog_pond 20 | p = FrogPond.new(1) 21 | assert_equal Frog, p.instance_variable_get(:@animals)[0].class 22 | p.simulate_one_day 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /13/ex5_plants.rb: -------------------------------------------------------------------------------- 1 | class Algae 2 | def initialize(name) 3 | @name = name 4 | end 5 | 6 | def grow 7 | puts("The Algae #{@name} soaks up the sun and grows") 8 | end 9 | end 10 | 11 | class WaterLily 12 | def initialize(name) 13 | @name = name 14 | end 15 | 16 | def grow 17 | puts("The water lily #{@name} floats, soaks up the sun and grows") 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /13/ex5_plants_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex5_plants' 7 | 8 | class PlantsTest < Test::Unit::TestCase 9 | 10 | def test_algae 11 | a = Algae.new('kelp') 12 | assert_equal 'kelp', a.instance_variable_get(:@name) 13 | a.grow 14 | end 15 | 16 | def test_lily 17 | l = WaterLily.new('gilded') 18 | assert_equal 'gilded', l.instance_variable_get(:@name) 19 | l.grow 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /13/ex6_extended_pond.rb: -------------------------------------------------------------------------------- 1 | class Pond 2 | def initialize(number_animals, number_plants) 3 | @animals = [] 4 | number_animals.times do |i| 5 | animal = new_animal("Animal#{i}") 6 | @animals << animal 7 | end 8 | 9 | @plants = [] 10 | number_plants.times do |i| 11 | plant = new_plant("Plant#{i}") 12 | @plants << plant 13 | end 14 | end 15 | 16 | def simulate_one_day 17 | @plants.each {|plant| plant.grow } 18 | @animals.each {|animal| animal.speak} 19 | @animals.each {|animal| animal.eat} 20 | @animals.each {|animal| animal.sleep} 21 | end 22 | end 23 | 24 | 25 | class DuckWaterLilyPond < Pond 26 | def new_animal(name) 27 | Duck.new(name) 28 | end 29 | 30 | def new_plant(name) 31 | WaterLily.new(name) 32 | end 33 | end 34 | 35 | class FrogAlgaePond < Pond 36 | def new_animal(name) 37 | Frog.new(name) 38 | end 39 | 40 | def new_plant(name) 41 | Algae.new(name) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /13/ex6_extended_pond_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_duck' 7 | require_relative 'ex3_frog' 8 | require_relative 'ex5_plants' 9 | require_relative 'ex6_extended_pond' 10 | 11 | class ExtendedPondTest < Test::Unit::TestCase 12 | 13 | 14 | def test_duck_lily_pond 15 | p = DuckWaterLilyPond.new(3, 4) 16 | assert_equal Array, p.instance_variable_get(:@animals).class 17 | assert_equal 3, p.instance_variable_get(:@animals).size 18 | assert_equal Array, p.instance_variable_get(:@plants).class 19 | assert_equal 4, p.instance_variable_get(:@plants).size 20 | p.simulate_one_day 21 | end 22 | 23 | def test_frog_algae_pond 24 | p = FrogAlgaePond.new(3, 4) 25 | assert_equal Array, p.instance_variable_get(:@animals).class 26 | assert_equal 3, p.instance_variable_get(:@animals).size 27 | assert_equal Array, p.instance_variable_get(:@plants).class 28 | assert_equal 4, p.instance_variable_get(:@plants).size 29 | p.simulate_one_day 30 | end 31 | 32 | end 33 | -------------------------------------------------------------------------------- /13/ex7_param_pond.rb: -------------------------------------------------------------------------------- 1 | class Pond 2 | def initialize(number_animals, number_plants) 3 | @animals = [] 4 | number_animals.times do |i| 5 | animal = new_organism(:animal, "Animal#{i}") 6 | @animals << animal 7 | end 8 | 9 | @plants = [] 10 | number_plants.times do |i| 11 | plant = new_organism(:plant, "Plant#{i}") 12 | @plants << plant 13 | end 14 | end 15 | 16 | # ... 17 | 18 | def simulate_one_day 19 | @plants.each {|plant| plant.grow} 20 | @animals.each {|animal| animal.speak} 21 | @animals.each {|animal| animal.eat} 22 | @animals.each {|animal| animal.sleep} 23 | end 24 | end 25 | 26 | class DuckWaterLilyPond < Pond 27 | def new_organism(type, name) 28 | if type == :animal 29 | Duck.new(name) 30 | elsif type == :plant 31 | WaterLily.new(name) 32 | else 33 | raise "Unknown organism type: #{type}" 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /13/ex7_param_pond_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_duck' 7 | require_relative 'ex3_frog' 8 | require_relative 'ex5_plants' 9 | require_relative 'ex7_param_pond' 10 | 11 | class ParameterizedPondTest < Test::Unit::TestCase 12 | 13 | def test_duck_lily_pond 14 | p = DuckWaterLilyPond.new(3, 4) 15 | assert_equal Array, p.instance_variable_get(:@animals).class 16 | assert_equal 3, p.instance_variable_get(:@animals).size 17 | assert_equal Array, p.instance_variable_get(:@plants).class 18 | assert_equal 4, p.instance_variable_get(:@plants).size 19 | p.simulate_one_day 20 | end 21 | 22 | def test_factory_method 23 | p = DuckWaterLilyPond.new(3, 4) 24 | assert_equal Duck, p.new_organism(:animal, 'fred').class 25 | assert_equal WaterLily, p.new_organism(:plant, 'gilded').class 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /13/ex8_factory_classes.rb: -------------------------------------------------------------------------------- 1 | 2 | class Pond 3 | def initialize(number_animals, animal_class, 4 | number_plants, plant_class) 5 | @animal_class = animal_class 6 | @plant_class = plant_class 7 | 8 | @animals = [] 9 | number_animals.times do |i| 10 | animal = new_organism(:animal, "Animal#{i}") 11 | @animals << animal 12 | end 13 | 14 | @plants = [] 15 | number_plants.times do |i| 16 | plant = new_organism(:plant, "Plant#{i}") 17 | @plants << plant 18 | end 19 | end 20 | 21 | def simulate_one_day 22 | @plants.each {|plant| plant.grow} 23 | @animals.each {|animal| animal.speak} 24 | @animals.each {|animal| animal.eat} 25 | @animals.each {|animal| animal.sleep} 26 | end 27 | 28 | def new_organism(type, name) 29 | if type == :animal 30 | @animal_class.new(name) 31 | elsif type == :plant 32 | @plant_class.new(name) 33 | else 34 | raise "Unknown organism type: #{type}" 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /13/ex8_factory_classes_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require_relative 'ex1_duck' 5 | require_relative 'ex3_frog' 6 | require_relative 'ex5_plants' 7 | require_relative 'ex8_factory_classes' 8 | 9 | example %q{ 10 | pond = Pond.new(3, Duck, 2, WaterLily) 11 | pond.simulate_one_day 12 | } 13 | 14 | -------------------------------------------------------------------------------- /13/ex8_factory_classes_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_duck' 7 | require_relative 'ex3_frog' 8 | require_relative 'ex5_plants' 9 | require_relative 'ex8_factory_classes' 10 | 11 | class ClassBasedFactoryMethodPondTest < Test::Unit::TestCase 12 | 13 | def test_pond 14 | p = Pond.new(3, Frog, 4, Algae) 15 | assert_equal Array, p.instance_variable_get(:@animals).class 16 | assert_equal Frog, p.instance_variable_get(:@animals)[0].class 17 | assert_equal 3, p.instance_variable_get(:@animals).size 18 | 19 | assert_equal Array, p.instance_variable_get(:@plants).class 20 | assert_equal Algae, p.instance_variable_get(:@plants)[0].class 21 | assert_equal 4, p.instance_variable_get(:@plants).size 22 | p.simulate_one_day 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /13/ex9_tiger_tree.rb: -------------------------------------------------------------------------------- 1 | class Tree 2 | def initialize(name) 3 | @name = name 4 | end 5 | 6 | def grow 7 | puts("The tree #{@name} grows tall") 8 | end 9 | end 10 | 11 | class Tiger 12 | def initialize(name) 13 | @name = name 14 | end 15 | 16 | def eat 17 | puts("Tiger #{@name} is eating anything it wants.") 18 | end 19 | 20 | def speak 21 | puts("Tiger #{@name} Roars!") 22 | end 23 | 24 | def sleep 25 | puts("Tiger #{@name} sleeps anywhere it wants.") 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /13/ex9_tiger_tree_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex9_tiger_tree' 7 | 8 | class TigerTreeTest < Test::Unit::TestCase 9 | 10 | def test_tree 11 | t = Tree.new('oak') 12 | assert_equal 'oak', t.instance_variable_get(:@name) 13 | t.grow 14 | end 15 | 16 | def test_tiger 17 | t = Tiger.new('tony') 18 | assert_equal 'tony', t.instance_variable_get(:@name) 19 | t.eat 20 | t.speak 21 | t.sleep 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /14/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /14/book.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/russolsen/design_patterns_in_ruby_code/f62eaf1cd9d53d68ba6a87e0f9b81d635ee89ec2/14/book.doc -------------------------------------------------------------------------------- /14/ex1_computer.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Computer 4 | attr_accessor :display 5 | attr_accessor :motherboard 6 | attr_reader :drives 7 | 8 | def initialize(display=:crt, motherboard=Motherboard.new, drives=[]) 9 | @motherboard = motherboard 10 | @drives = drives 11 | @display = display 12 | end 13 | end 14 | 15 | class CPU 16 | # Common CPU stuff... 17 | end 18 | 19 | class BasicCPU < CPU 20 | # Lots of not very fast CPU related stuff... 21 | end 22 | 23 | class TurboCPU < CPU 24 | # Lots of very fast CPU stuff... 25 | end 26 | 27 | class Motherboard 28 | attr_accessor :cpu 29 | attr_accessor :memory_size 30 | 31 | def initialize(cpu=BasicCPU.new, memory_size=1000) 32 | @cpu = cpu 33 | @memory_size = memory_size 34 | end 35 | end 36 | 37 | 38 | class Drive 39 | attr_reader :type # either :hard_disk, :cd or :dvd 40 | attr_reader :size # in Mb 41 | attr_reader :writable # true if this drive is writable 42 | 43 | def initialize(type, size, writable) 44 | @type = type 45 | @size = size 46 | @writable = writable 47 | end 48 | end 49 | 50 | -------------------------------------------------------------------------------- /14/ex1_computer_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex1_computer' 6 | 7 | 8 | example %q{ 9 | 10 | # Build a fast computer with lots of memory... 11 | 12 | motherboard = Motherboard.new(TurboCPU.new, 4000) 13 | 14 | # ... and a hard drive, CD writer and a DVD 15 | 16 | drives = [] 17 | drives << Drive.new(:hard_drive, 200000, true) 18 | drives << Drive.new(:cd, 760, true) 19 | drives << Drive.new(:dvd, 4700, false) 20 | 21 | computer = Computer.new(:lcd, motherboard, drives) 22 | 23 | 24 | pp computer 25 | } 26 | 27 | -------------------------------------------------------------------------------- /14/ex2_simple_builder.rb: -------------------------------------------------------------------------------- 1 | 2 | class ComputerBuilder 3 | attr_reader :computer 4 | 5 | def initialize 6 | @computer = Computer.new 7 | end 8 | 9 | def turbo(has_turbo_cpu=true) 10 | @computer.motherboard.cpu = TurboCPU.new 11 | end 12 | 13 | def display=(display) 14 | @computer.display=display 15 | end 16 | 17 | def memory_size=(size_in_mb) 18 | @computer.motherboard.memory_size = size_in_mb 19 | end 20 | 21 | def add_cd(writer=false) 22 | @computer.drives << Drive.new(:cd, 760, writer) 23 | end 24 | 25 | def add_dvd(writer=false) 26 | @computer.drives << Drive.new(:dvd, 4000, writer) 27 | end 28 | 29 | def add_hard_disk(size_in_mb) 30 | @computer.drives << Drive.new(:hard_disk, size_in_mb, true) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /14/ex2_simple_builder_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex1_computer' 6 | require_relative 'ex2_simple_builder' 7 | 8 | 9 | example %q{ 10 | 11 | builder = ComputerBuilder.new 12 | builder.turbo 13 | builder.add_cd(true) 14 | builder.add_dvd 15 | builder.add_hard_disk(100000) 16 | 17 | computer = builder.computer 18 | 19 | 20 | 21 | pp computer 22 | } 23 | 24 | -------------------------------------------------------------------------------- /14/ex4_sane_builder.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class ComputerBuilder 4 | 5 | def computer 6 | raise "Not enough memory" if @computer.motherboard.memory_size < 250 7 | raise "Too many drives" if @computer.drives.size > 4 8 | hard_disk = @computer.drives.find {|drive| drive.type == :hard_disk} 9 | raise "No hard disk." unless hard_disk 10 | @computer 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /14/ex4_sane_builder_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require_relative 'ex1_computer' 5 | require_relative 'ex2_simple_builder' 6 | require_relative 'ex4_sane_builder' 7 | 8 | 9 | class SaneBuilderTest < Test::Unit::TestCase 10 | 11 | def test_memory_check 12 | builder = ComputerBuilder.new 13 | builder.memory_size=1 14 | assert_raise(RuntimeError){ builder.computer } 15 | end 16 | 17 | def test_number_drive_check 18 | builder = ComputerBuilder.new 19 | 5.times { builder.add_hard_disk(500) } 20 | assert_raise(RuntimeError){ builder.computer } 21 | end 22 | 23 | def test_hard_disk_check 24 | builder = ComputerBuilder.new 25 | 4.times { builder.add_dvd } 26 | assert_raise(RuntimeError){ builder.computer } 27 | end 28 | 29 | 30 | end 31 | 32 | -------------------------------------------------------------------------------- /14/ex5_saner_builder.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | class ComputerBuilder 5 | 6 | def computer 7 | raise "Not enough memory" if @computer.motherboard.memory_size < 250 8 | raise "Too many drives" if @computer.drives.size > 4 9 | hard_disk = @computer.drives.find {|drive| drive.type == :hard_disk} 10 | 11 | 12 | if ! hard_disk 13 | raise "No room to add hard disk." if @computer.drives.size >= 4 14 | add_hard_disk(100000) 15 | end 16 | 17 | 18 | @computer 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /14/ex5_saner_builder_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require_relative 'ex1_computer' 5 | require_relative 'ex2_simple_builder' 6 | require_relative 'ex5_saner_builder' 7 | 8 | 9 | class SanerBuilderTest < Test::Unit::TestCase 10 | 11 | def test_hard_disk_check 12 | builder = ComputerBuilder.new 13 | computer = builder.computer 14 | assert_equal 1, computer.drives.size 15 | assert_equal :hard_disk, computer.drives[0].type 16 | end 17 | 18 | 19 | end 20 | 21 | 22 | -------------------------------------------------------------------------------- /14/ex6_reuse_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | require 'pp' 5 | 6 | require_relative 'ex1_computer' 7 | require_relative 'ex3_polymorphic' 8 | 9 | example %q{ 10 | 11 | builder = LaptopBuilder.new 12 | builder.add_hard_disk(100000) 13 | builder.turbo 14 | 15 | computer1 = builder.computer 16 | computer2 = builder.computer 17 | 18 | 19 | puts "Should be the same:" 20 | puts computer1 21 | puts computer2 22 | } 23 | -------------------------------------------------------------------------------- /14/ex7_reset_builder.rb: -------------------------------------------------------------------------------- 1 | 2 | class LaptopBuilder 3 | 4 | # Lots of code omitted ... 5 | 6 | def reset 7 | @computer = LaptopComputer.new 8 | end 9 | 10 | end 11 | 12 | 13 | -------------------------------------------------------------------------------- /14/ex7_reset_builder_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require_relative 'ex1_computer' 5 | require_relative 'ex3_polymorphic' 6 | require_relative 'ex7_reset_builder' 7 | 8 | 9 | class ResetBuilderTest < Test::Unit::TestCase 10 | 11 | def test_reset 12 | builder = LaptopBuilder.new 13 | builder.add_hard_disk(100000) 14 | builder.turbo 15 | computer1 = builder.computer 16 | 17 | assert_equal computer1.drives[0].type, :hard_disk 18 | assert_equal computer1.drives[0].size, 100000 19 | assert_equal computer1.motherboard.memory_size, 1000 20 | 21 | builder.reset 22 | builder.add_hard_disk(50000) 23 | builder.memory_size=500 24 | computer2 = builder.computer 25 | 26 | assert_not_equal computer1, computer2 27 | 28 | assert_equal computer2.drives[0].type, :hard_disk 29 | assert_equal computer2.drives[0].size, 50000 30 | assert_equal computer2.motherboard.memory_size, 500 31 | end 32 | 33 | end 34 | 35 | 36 | -------------------------------------------------------------------------------- /14/ex8_magic_method.rb: -------------------------------------------------------------------------------- 1 | 2 | class ComputerBuilder 3 | 4 | def method_missing(name, *args) 5 | words = name.to_s.split("_") 6 | return super(name, *args) unless words.shift == 'add' 7 | words.each do |word| 8 | next if word == 'and' 9 | add_cd if word == 'cd' 10 | add_dvd if word == 'dvd' 11 | add_hard_disk(100000) if word == 'harddisk' 12 | turbo if word == 'turbo' 13 | end 14 | end 15 | 16 | end 17 | 18 | 19 | -------------------------------------------------------------------------------- /14/ex8_magic_method_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex1_computer' 6 | require_relative 'ex2_simple_builder' 7 | require_relative 'ex8_magic_method' 8 | 9 | 10 | 11 | example %q{ 12 | builder = ComputerBuilder.new 13 | builder.add_dvd_and_harddisk 14 | 15 | pp builder.computer 16 | puts 17 | 18 | builder = ComputerBuilder.new 19 | builder.add_turbo_and_dvd_and_harddisk 20 | 21 | pp builder.computer 22 | } 23 | -------------------------------------------------------------------------------- /14/ex8_magic_method_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require_relative 'ex1_computer' 5 | require_relative 'ex2_simple_builder' 6 | require_relative 'ex8_magic_method' 7 | 8 | 9 | class MagicMethodTest < Test::Unit::TestCase 10 | 11 | def test_mm 12 | builder = ComputerBuilder.new 13 | builder.add_turbo_and_dvd_and_cd_and_harddisk 14 | computer = builder.computer 15 | 16 | assert_equal computer.drives[0].type, :dvd 17 | assert_equal computer.drives[1].type, :cd 18 | assert_equal computer.drives[2].type, :hard_disk 19 | assert_equal computer.drives[2].size, 100000 20 | assert_equal computer.motherboard.cpu.class, TurboCPU 21 | end 22 | 23 | end 24 | 25 | 26 | -------------------------------------------------------------------------------- /14/ex9_mail_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | 5 | example %q{ 6 | 7 | require 'rubygems' 8 | require 'mailfactory' 9 | 10 | mail_builder = MailFactory.new 11 | mail_builder.to='russ@russolsen.com' 12 | mail_builder.from='russ@russolsen.com' 13 | mail_builder.subject='The document' 14 | mail_builder.text='Here is that document you wanted' 15 | mail_builder.attach('book.doc') 16 | 17 | 18 | 19 | puts mail_builder 20 | } 21 | 22 | -------------------------------------------------------------------------------- /15/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /15/dir1/small.mp3: -------------------------------------------------------------------------------- 1 | small 2 | -------------------------------------------------------------------------------- /15/dir1/small1.txt: -------------------------------------------------------------------------------- 1 | small 2 | -------------------------------------------------------------------------------- /15/dir1/small2.txt: -------------------------------------------------------------------------------- 1 | small 2 | -------------------------------------------------------------------------------- /15/dir2/small.mp3: -------------------------------------------------------------------------------- 1 | small 2 | -------------------------------------------------------------------------------- /15/dir2/small1.txt: -------------------------------------------------------------------------------- 1 | small 2 | -------------------------------------------------------------------------------- /15/dir2/small2.txt: -------------------------------------------------------------------------------- 1 | small 2 | -------------------------------------------------------------------------------- /15/dir2/subdir/small.jpg: -------------------------------------------------------------------------------- 1 | small 2 | -------------------------------------------------------------------------------- /15/ex2_parser.rb: -------------------------------------------------------------------------------- 1 | 2 | class Parser 3 | def initialize(text) 4 | @tokens = text.scan(/\(|\)|[\w\.\*]+/) 5 | end 6 | 7 | def next_token 8 | @tokens.shift 9 | end 10 | 11 | def expression 12 | token = next_token 13 | 14 | if token == nil 15 | return nil 16 | 17 | elsif token == '(' 18 | result = expression 19 | raise 'Expected )' unless next_token == ')' 20 | result 21 | 22 | elsif token == 'all' 23 | return All.new 24 | 25 | elsif token == 'writable' 26 | return Writable.new 27 | 28 | elsif token == 'bigger' 29 | return Bigger.new(next_token.to_i) 30 | 31 | elsif token == 'filename' 32 | return FileName.new(next_token) 33 | 34 | elsif token == 'not' 35 | return Not.new(expression) 36 | 37 | elsif token == 'and' 38 | return And.new(expression, expression) 39 | 40 | elsif token == 'or' 41 | return Or.new(expression, expression) 42 | 43 | else 44 | raise "Unexpected token: #{token}" 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /15/ex2_parser_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex1_files' 6 | require_relative 'ex2_parser' 7 | 8 | 9 | example %q{ 10 | 11 | parser = Parser.new "and (and(bigger 1024)(filename *.mp3)) writable" 12 | ast = parser.expression 13 | 14 | 15 | pp ast 16 | } 17 | 18 | -------------------------------------------------------------------------------- /15/ex3_operators.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Expression 4 | 5 | def |(other) 6 | Or.new(self, other) 7 | end 8 | 9 | def &(other) 10 | And.new(self, other) 11 | end 12 | 13 | end 14 | 15 | 16 | def all 17 | All.new 18 | end 19 | 20 | def bigger(size) 21 | Bigger.new(size) 22 | end 23 | 24 | def file_name(pattern) 25 | FileName.new(pattern) 26 | end 27 | 28 | def except(expression) 29 | Not.new(expression) 30 | end 31 | 32 | def writable 33 | Writable.new 34 | end 35 | 36 | -------------------------------------------------------------------------------- /15/ex3_operators_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | require_relative 'ex1_files' 6 | require_relative 'ex3_operators' 7 | 8 | 9 | example %q{ 10 | 11 | Or.new( 12 | And.new(Bigger.new(2000), Not.new(Writable.new)), 13 | FileName.new('*.mp3')) 14 | 15 | } 16 | 17 | 18 | example %q{ 19 | (Bigger.new(2000) & Not.new(Writable.new)) | FileName.new('*.mp3') 20 | } 21 | 22 | example %q{ 23 | (bigger(2000) & except(writable) ) | file_name('*.mp3') 24 | } 25 | 26 | -------------------------------------------------------------------------------- /15/ex4_speed_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../example' 4 | 5 | 6 | class Add 7 | 8 | def initialize(a, b) 9 | @a = a 10 | @b = b 11 | end 12 | 13 | def interpret 14 | @a.interpret + @b.interpret 15 | end 16 | 17 | end 18 | 19 | class Constant 20 | 21 | def initialize(value) 22 | @value = value 23 | end 24 | 25 | def interpret 26 | @value 27 | end 28 | 29 | end 30 | 31 | example %q{ 32 | 33 | puts \ 34 | Add.new(Constant.new(2), Constant.new(2)).interpret 35 | 36 | 37 | puts \ 38 | 2 + 2 39 | 40 | } 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /15/ex5_runt_demo.broken: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require '../example' 4 | 5 | # This example is broken because Runt no longer seems to load. 6 | 7 | example %Q{ 8 | require 'rubygems' 9 | require 'runt' 10 | 11 | mondays = Runt::DIWeek.new(Runt::Monday) 12 | wednesdays = Runt::DIWeek.new(Runt::Wednesday) 13 | fridays = Runt::DIWeek.new(Runt::Friday) 14 | 15 | puts "check xmas" 16 | puts mondays.include?(Date.new(2015,12,25)) 17 | puts wednesdays.include?(Date.new(2015,12,25)) 18 | puts fridays.include?(Date.new(2015,12,25)) 19 | 20 | nine_to_twelve = Runt::REDay.new(9,0,12,0) 21 | 22 | puts nine_to_twelve.include?(DateTime.new(2009,9,14,8,30)) 23 | puts nine_to_twelve.include?(DateTime.new(2009,9,14,9,5)) 24 | puts nine_to_twelve.include?(DateTime.new(2009,9,14,12,1)) 25 | 26 | class_times = (mondays | wednesdays | fridays) & nine_to_twelve 27 | } 28 | -------------------------------------------------------------------------------- /15/test_dir/small.mp3: -------------------------------------------------------------------------------- 1 | small 2 | -------------------------------------------------------------------------------- /15/test_dir/small1.txt: -------------------------------------------------------------------------------- 1 | small 2 | -------------------------------------------------------------------------------- /15/test_dir/small2.txt: -------------------------------------------------------------------------------- 1 | small 2 | -------------------------------------------------------------------------------- /15/test_dir/subdir/small.jpg: -------------------------------------------------------------------------------- 1 | small 2 | -------------------------------------------------------------------------------- /16/Rakefile: -------------------------------------------------------------------------------- 1 | 2 | task :default do 3 | puts "The examples in this directory do" 4 | puts "not really lend themselves to quick testing" 5 | end 6 | 7 | task :clean 8 | -------------------------------------------------------------------------------- /16/dir1/a: -------------------------------------------------------------------------------- 1 | total 0 2 | -rw-r--r-- 1 russ users 0 2007-08-06 23:34 a 3 | -------------------------------------------------------------------------------- /16/dir1/b: -------------------------------------------------------------------------------- 1 | hello 2 | -------------------------------------------------------------------------------- /16/dir1/poem.txt: -------------------------------------------------------------------------------- 1 | the rain in spain 2 | -------------------------------------------------------------------------------- /16/dir1/subdir/poem.txt: -------------------------------------------------------------------------------- 1 | the rain in spain 2 | -------------------------------------------------------------------------------- /16/dir1/subdir/x: -------------------------------------------------------------------------------- 1 | total 0 2 | -rw-r--r-- 1 russ users 0 2007-08-06 23:34 a 3 | -------------------------------------------------------------------------------- /16/dir1/subdir/y: -------------------------------------------------------------------------------- 1 | hello 2 | -------------------------------------------------------------------------------- /16/first_example/backup.pr: -------------------------------------------------------------------------------- 1 | 2 | backup 'data/documents' 3 | 4 | backup 'data/music', file_name('*.mp3') & file_name('*.wav') 5 | 6 | backup 'data/images', except(file_name('*.tmp')) 7 | 8 | to '/external_drive/backups' 9 | 10 | interval 0.05 11 | -------------------------------------------------------------------------------- /16/first_example/data/documents/speach.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/russolsen/design_patterns_in_ruby_code/f62eaf1cd9d53d68ba6a87e0f9b81d635ee89ec2/16/first_example/data/documents/speach.txt -------------------------------------------------------------------------------- /16/first_example/data/images/mona.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/russolsen/design_patterns_in_ruby_code/f62eaf1cd9d53d68ba6a87e0f9b81d635ee89ec2/16/first_example/data/images/mona.jpeg -------------------------------------------------------------------------------- /16/first_example/data/music/9th.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/russolsen/design_patterns_in_ruby_code/f62eaf1cd9d53d68ba6a87e0f9b81d635ee89ec2/16/first_example/data/music/9th.mp3 -------------------------------------------------------------------------------- /16/first_example/ex2_pr_defs_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../../example' 4 | 5 | example %q{ 6 | 7 | require_relative 'finder' 8 | 9 | def backup(dir, find_expression=All.new) 10 | puts "Backup called, source dir=#{dir} find expr=#{find_expression}" 11 | end 12 | 13 | def to(backup_directory) 14 | puts "To called, backup dir=#{backup_directory}" 15 | end 16 | 17 | def interval(minutes) 18 | puts "Interval called, interval = #{minutes} minutes" 19 | end 20 | 21 | eval(File.read('backup.pr')) 22 | 23 | } 24 | -------------------------------------------------------------------------------- /16/first_example/ex4_real_defs.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | def backup(dir, find_expression=All.new) 4 | Backup.instance.data_sources << DataSource.new(dir, find_expression) 5 | end 6 | 7 | def to(backup_directory) 8 | Backup.instance.backup_directory = backup_directory 9 | end 10 | 11 | def interval(minutes) 12 | Backup.instance.interval = minutes 13 | end 14 | 15 | eval(File.read('backup.pr')) 16 | 17 | Backup.instance.run 18 | 19 | -------------------------------------------------------------------------------- /16/first_example/ex4_real_defs_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative 'timeout' 4 | require_relative '../../example' 5 | 6 | begin 7 | Timeout.timeout(5) do 8 | require_relative 'finder' 9 | require_relative 'ex3_packrat' 10 | require_relative 'ex4_real_defs' 11 | end 12 | rescue Timeout::Error 13 | puts "All done" 14 | end 15 | 16 | -------------------------------------------------------------------------------- /16/first_example/finder.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../15/ex1_files' 2 | require_relative '../../15/ex3_operators' 3 | 4 | -------------------------------------------------------------------------------- /16/second_example/backup.pr: -------------------------------------------------------------------------------- 1 | 2 | 3 | Backup.new do |b| 4 | b.backup 'data/oldies', file_name('*.mp3') | file_name('*.wav') 5 | b.to 'backup' 6 | b.interval 0.05 7 | end 8 | 9 | Backup.new do |b| 10 | b.backup 'data/newies', file_name('*.mp3') | file_name('*.wav') 11 | b.to 'backup' 12 | b.interval 0.05 13 | end 14 | -------------------------------------------------------------------------------- /16/second_example/backup.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../../example' 4 | 5 | require_relative 'finder' 6 | require_relative 'ex6_multi_backup' 7 | 8 | -------------------------------------------------------------------------------- /16/second_example/ex5_full_parser.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../../example' 4 | 5 | 6 | 7 | require_relative 'finder' 8 | 9 | def backup(*args) 10 | pp args 11 | end 12 | 13 | alias to backup 14 | alias interval backup 15 | 16 | 17 | example %q{ 18 | 19 | backup '/home/russ/bob\'s_documents' 20 | 21 | backup "/home/russ/bob's_documents" 22 | } 23 | 24 | 25 | example %q{ 26 | 27 | # 28 | # Back up Bob's directory 29 | # 30 | backup "/home/russ/bob's_documents" 31 | } 32 | 33 | 34 | example %q{ 35 | # 36 | # A file finding expression for music files 37 | # 38 | music_files = file_name('*.mp3') | file_name('*.wav') 39 | 40 | # 41 | # Backup my two music directories 42 | # 43 | backup '/home/russ/oldies', music_files 44 | backup '/home/russ/newies', music_files 45 | 46 | to '/tmp/backup' 47 | 48 | interval 60 49 | } 50 | -------------------------------------------------------------------------------- /16/second_example/ex6_multi_backup_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative '../../example' 4 | 5 | require_relative 'finder' 6 | 7 | require_relative 'ex6_multi_backup' 8 | 9 | -------------------------------------------------------------------------------- /16/second_example/finder.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../15/ex1_files' 2 | require_relative '../../15/ex3_operators' 3 | 4 | -------------------------------------------------------------------------------- /17/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /17/ex10_account_attr.rb: -------------------------------------------------------------------------------- 1 | class BankAccount 2 | attr_accessor :balance 3 | 4 | def initialize(opening_balance) 5 | @balance = opening_balance 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /17/ex10_account_attr_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex10_account_attr' 7 | 8 | class BankAccountTest < Test::Unit::TestCase 9 | 10 | def test_account 11 | a = BankAccount.new(100) 12 | assert_equal 100, a.balance 13 | 14 | a.balance = 44 15 | assert_equal 44, a.balance 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /17/ex11_readable.rb: -------------------------------------------------------------------------------- 1 | class Object 2 | def self.readable_attribute(name) 3 | code = %Q{ 4 | def #{name} 5 | @#{name} 6 | end 7 | } 8 | class_eval(code) 9 | end 10 | end 11 | 12 | class BankAccount 13 | readable_attribute :balance 14 | 15 | def initialize(balance) 16 | @balance = balance 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /17/ex11_readable_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex11_readable' 7 | 8 | class BankAccountTest < Test::Unit::TestCase 9 | 10 | def test_account 11 | a = BankAccount.new(100) 12 | assert_equal 100, a.balance 13 | a = BankAccount.new(99) 14 | assert_equal 99, a.balance 15 | 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /17/ex12_forwardable_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require 'forwardable' 5 | 6 | example %q{ 7 | class Engine 8 | def start_engine 9 | # Start the engine... 10 | end 11 | 12 | def stop_engine 13 | # Stop the engine 14 | end 15 | end 16 | 17 | class Car 18 | extend Forwardable 19 | 20 | def_delegators :@engine, :start_engine, :stop_engine 21 | 22 | def initialize 23 | @engine = Engine.new 24 | end 25 | end 26 | 27 | car = Car.new 28 | car.start_engine 29 | car.stop_engine 30 | } 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /17/ex13_active_record.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module ActiveRecord 4 | class Base 5 | def self.has_many(other) 6 | puts "#{self} has many #{other}" 7 | end 8 | 9 | def self.belongs_to(other) 10 | puts "#{self} belongs to #{other}" 11 | end 12 | 13 | def self.has_one(other) 14 | puts "#{self} has one #{other}" 15 | end 16 | end 17 | end 18 | 19 | 20 | class Animal < ActiveRecord::Base 21 | has_one :description 22 | end 23 | 24 | class Description < ActiveRecord::Base 25 | belongs_to :animal 26 | end 27 | 28 | class Animal < ActiveRecord::Base 29 | has_one :description 30 | belongs_to :species 31 | end 32 | 33 | class Species < ActiveRecord::Base 34 | has_many :animals 35 | end 36 | -------------------------------------------------------------------------------- /17/ex1_method.rb: -------------------------------------------------------------------------------- 1 | 2 | def new_plant(stem_type, leaf_type) 3 | plant = Object.new 4 | 5 | if stem_type == :fleshy 6 | def plant.stem 7 | 'fleshy' 8 | end 9 | else 10 | def plant.stem 11 | 'woody' 12 | end 13 | end 14 | 15 | if leaf_type == :broad 16 | def plant.leaf 17 | 'broad' 18 | end 19 | else 20 | def plant.leaf 21 | 'needle' 22 | end 23 | end 24 | 25 | plant 26 | end 27 | -------------------------------------------------------------------------------- /17/ex1_method_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require_relative 'ex1_method' 5 | 6 | example %q{ 7 | plant1 = new_plant(:fleshy, :broad) 8 | plant2 = new_plant(:woody, :needle) 9 | 10 | puts "Plant 1's stem: #{plant1.stem} leaf: #{plant1.leaf}" 11 | puts "Plant 2's stem: #{plant2.stem} leaf: #{plant2.leaf}" 12 | } 13 | 14 | -------------------------------------------------------------------------------- /17/ex1_method_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_method' 7 | 8 | class MethodByMethodTest < Test::Unit::TestCase 9 | 10 | 11 | def test_plant 12 | p = new_plant(:fleshy, :broad) 13 | assert_equal "fleshy", p.stem 14 | assert_equal "broad", p.leaf 15 | 16 | p = new_plant(:woody, :needle) 17 | assert_equal "woody", p.stem 18 | assert_equal "needle", p.leaf 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /17/ex2_module.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module Carnivore 4 | def diet 5 | 'meat' 6 | end 7 | 8 | def teeth 9 | 'sharp' 10 | end 11 | end 12 | 13 | module Herbivore 14 | def diet 15 | 'plant' 16 | end 17 | 18 | def teeth 19 | 'flat' 20 | end 21 | end 22 | 23 | module Nocturnal 24 | def sleep_time 25 | 'day' 26 | end 27 | 28 | def awake_time 29 | 'night' 30 | end 31 | end 32 | 33 | module Diurnal 34 | def sleep_time 35 | 'night' 36 | end 37 | 38 | def awake_time 39 | 'day' 40 | end 41 | end 42 | 43 | 44 | 45 | def new_animal(diet, awake) 46 | animal = Object.new 47 | 48 | if diet == :meat 49 | animal.extend(Carnivore) 50 | else 51 | animal.extend(Herbivore) 52 | end 53 | 54 | if awake == :day 55 | animal.extend(Diurnal) 56 | else 57 | animal.extend(Nocturnal) 58 | end 59 | 60 | animal 61 | end 62 | 63 | #leopard = new_animal( :meat, :night) 64 | #cow = new_animal( :plant, :day) 65 | 66 | #leopard.describe 67 | #cow.describe 68 | 69 | -------------------------------------------------------------------------------- /17/ex2_module_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex2_module' 7 | 8 | class MethodByMethodTest < Test::Unit::TestCase 9 | 10 | 11 | def test_new_animal_leopard 12 | leopard = new_animal(:meat, :night) 13 | assert leopard.kind_of?(Nocturnal) 14 | assert leopard.kind_of?(Carnivore) 15 | assert_equal 'sharp', leopard.teeth 16 | assert_equal 'meat', leopard.diet 17 | assert_equal 'day', leopard.sleep_time 18 | assert_equal 'night', leopard.awake_time 19 | end 20 | 21 | def test_new_animal_cow 22 | cow = new_animal(:plant, :day) 23 | assert cow.kind_of?(Diurnal) 24 | assert cow.kind_of?(Herbivore) 25 | assert_equal 'flat', cow.teeth 26 | assert_equal 'plant', cow.diet 27 | assert_equal 'night', cow.sleep_time 28 | assert_equal 'day', cow.awake_time 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /17/ex3_members.rb: -------------------------------------------------------------------------------- 1 | 2 | class Tiger < CompositeBase 3 | member_of(:population) 4 | member_of(:classification) 5 | 6 | # Lots of code omitted ... 7 | end 8 | 9 | class Tree < CompositeBase 10 | member_of(:population) 11 | member_of(:classification) 12 | 13 | # Lots of code omitted ... 14 | end 15 | 16 | 17 | class Jungle < CompositeBase 18 | composite_of(:population) 19 | 20 | # Lots of code omitted ... 21 | end 22 | 23 | class Species < CompositeBase 24 | composite_of(:classification) 25 | 26 | # Lots of code omitted ... 27 | end 28 | 29 | 30 | 31 | class Region < CompositeBase 32 | composite_of(:population) 33 | 34 | # Lots of code omitted ... 35 | end 36 | 37 | class Genera < CompositeBase 38 | composite_of(:classification) 39 | 40 | # Lots of code omitted ... 41 | end 42 | -------------------------------------------------------------------------------- /17/ex4_base.rb: -------------------------------------------------------------------------------- 1 | class CompositeBase 2 | attr_reader :name 3 | 4 | def initialize(name) 5 | @name = name 6 | end 7 | 8 | def self.member_of(composite_name) 9 | code = %Q{ 10 | attr_accessor :parent_#{composite_name} 11 | } 12 | class_eval(code) 13 | end 14 | 15 | def self.composite_of(composite_name) 16 | member_of composite_name 17 | 18 | code = %Q{ 19 | def sub_#{composite_name}s 20 | @sub_#{composite_name}s = [] unless @sub_#{composite_name}s 21 | @sub_#{composite_name}s 22 | end 23 | 24 | def add_sub_#{composite_name}(child) 25 | return if sub_#{composite_name}s.include?(child) 26 | sub_#{composite_name}s << child 27 | child.parent_#{composite_name} = self 28 | end 29 | 30 | def delete_sub_#{composite_name}(child) 31 | return unless sub_#{composite_name}s.include?(child) 32 | sub_#{composite_name}s.delete(child) 33 | child.parent_#{composite_name} = nil 34 | end 35 | } 36 | class_eval(code) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /17/ex4_base_demo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../example' 3 | 4 | require_relative 'ex4_base' 5 | require_relative 'ex3_members' 6 | 7 | example %q{ 8 | 9 | tony_tiger = Tiger.new('tony') 10 | se_jungle = Jungle.new('southeastern jungle tigers') 11 | se_jungle.add_sub_population(tony_tiger) 12 | 13 | tony_tiger.parent_population # Should be the southeastern jungle 14 | } 15 | 16 | example %q{ 17 | se_jungle = Jungle.new('southeastern jungle tigers') 18 | 19 | 20 | sw_jungle = Jungle.new('southwestern jungle tigers') 21 | region = Region.new('south') 22 | region.add_sub_population(se_jungle) 23 | region.add_sub_population(sw_jungle) 24 | } 25 | 26 | 27 | example %q{ 28 | tony_tiger = Tiger.new('tony') 29 | 30 | species = Species.new('P. tigris') 31 | species.add_sub_classification(tony_tiger) 32 | 33 | tony_tiger.parent_classification # Should be P. tigris 34 | } 35 | -------------------------------------------------------------------------------- /17/ex5_member_of.rb: -------------------------------------------------------------------------------- 1 | 2 | def member_of_composite?(object, composite_name) 3 | public_methods = object.public_methods 4 | public_methods.include?("parent_#{composite_name}".to_sym) 5 | end 6 | -------------------------------------------------------------------------------- /17/ex5_member_of_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'test/unit' 3 | require 'pp' 4 | 5 | require_relative 'ex4_base' 6 | require_relative 'ex3_members' 7 | require_relative 'ex5_member_of' 8 | 9 | class MemberOfTest < Test::Unit::TestCase 10 | 11 | def test_tiger_and_jungle 12 | jungle = Jungle.new('southern jungle') 13 | tony = Tiger.new('tony') 14 | 15 | assert member_of_composite?(jungle, :population) 16 | assert ! member_of_composite?(jungle, :nonsense) 17 | assert member_of_composite?(tony, :population) 18 | assert member_of_composite?(tony, :classification) 19 | assert ! member_of_composite?(tony, :nonsense) 20 | assert ! member_of_composite?("hello", :population) 21 | assert ! member_of_composite?(44, :population) 22 | assert ! member_of_composite?(false, :population) 23 | assert ! member_of_composite?(nil, :population) 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /17/ex6_responds.rb: -------------------------------------------------------------------------------- 1 | 2 | def member_of_composite?(object, composite_name) 3 | object.respond_to?("parent_#{composite_name}") 4 | end 5 | -------------------------------------------------------------------------------- /17/ex6_responds_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'test/unit' 3 | require 'pp' 4 | 5 | require_relative 'ex4_base' 6 | require_relative 'ex3_members' 7 | require_relative 'ex5_member_of' 8 | 9 | class MemberOfTest < Test::Unit::TestCase 10 | 11 | def test_tiger_and_jungle 12 | jungle = Jungle.new('southern jungle') 13 | tony = Tiger.new('tony') 14 | 15 | assert member_of_composite?(jungle, :population) 16 | assert ! member_of_composite?(jungle, :nonsense) 17 | assert member_of_composite?(tony, :population) 18 | assert member_of_composite?(tony, :classification) 19 | assert ! member_of_composite?(tony, :nonsense) 20 | assert ! member_of_composite?("hello", :population) 21 | assert ! member_of_composite?(44, :population) 22 | assert ! member_of_composite?(false, :population) 23 | assert ! member_of_composite?(nil, :population) 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /17/ex7_lost_method.rb: -------------------------------------------------------------------------------- 1 | 2 | require_relative 'ex4_base' 3 | 4 | class Species < CompositeBase 5 | 6 | # This method is about to be lost! 7 | 8 | def parent_classification 9 | # ... 10 | end 11 | 12 | # And there it goes... 13 | 14 | composite_of(:classification) 15 | end 16 | -------------------------------------------------------------------------------- /17/ex8_lost_method_defense.rb: -------------------------------------------------------------------------------- 1 | class CompositeBase 2 | 3 | # ... 4 | 5 | def self.member_of(composite_name) 6 | attr_name = "parent_#{composite_name}" 7 | 8 | raise 'Method redefinition' if instance_methods.include?(attr_name) 9 | 10 | code = %Q{ 11 | attr_accessor :#{attr_name} 12 | } 13 | class_eval(code) 14 | end 15 | 16 | # ... 17 | 18 | end 19 | -------------------------------------------------------------------------------- /17/ex9_account_simple.rb: -------------------------------------------------------------------------------- 1 | 2 | class BankAccount 3 | def initialize(opening_balance) 4 | @balance = opening_balance 5 | end 6 | 7 | def balance 8 | @balance 9 | end 10 | 11 | def balance=(new_balance) 12 | @balance = new_balance 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /17/ex9_account_simple_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex9_account_simple' 7 | 8 | class BankAccountTest < Test::Unit::TestCase 9 | 10 | def test_account 11 | a = BankAccount.new(100) 12 | assert_equal 100, a.balance 13 | 14 | a.balance = 44 15 | assert_equal 44, a.balance 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /18/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /18/adapters.yml: -------------------------------------------------------------------------------- 1 | 2 | smtp: SmtpAdapter 3 | file: FileAdapter 4 | http: HttpAdapter 5 | -------------------------------------------------------------------------------- /18/ex1_message.rb: -------------------------------------------------------------------------------- 1 | require 'uri' 2 | 3 | class Message 4 | attr_accessor :from, :to, :body 5 | 6 | def initialize(from, to, body) 7 | @from = from 8 | @to = URI.parse(to) 9 | @body = body 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /18/ex1_message_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_message' 7 | 8 | class MessageTest < Test::Unit::TestCase 9 | 10 | def test_adapter_for_file 11 | message = Message.new('fred', 'http://russolsen.com/foo/bar', 'message') 12 | assert_equal 'fred', message.from 13 | assert_equal 'http', message.to.scheme 14 | assert_equal 'russolsen.com', message.to.host 15 | assert_equal '/foo/bar', message.to.path 16 | assert_equal 'message', message.body 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /18/ex3_hardcoded.rb: -------------------------------------------------------------------------------- 1 | def adapter_for(message) 2 | protocol = message.to.scheme 3 | return FileAdapter.new if protocol == 'file' 4 | return HttpAdapter.new if protocol == 'http' 5 | return SmtpAdapter.new if protocol == 'smtp' 6 | nil 7 | end 8 | -------------------------------------------------------------------------------- /18/ex3_hardcoded_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_message' 7 | require_relative 'ex2_adapters' 8 | require_relative 'ex3_hardcoded' 9 | 10 | class HardcodedChooserTest < Test::Unit::TestCase 11 | 12 | def test_adapter_for_file 13 | m = Message.new('fred', 'file:///foo/bar', 'message') 14 | a = adapter_for(m) 15 | assert_equal FileAdapter, a.class 16 | end 17 | 18 | def test_adapter_for_http 19 | m = Message.new('fred', 'http://www.yahoo.com:/foo/bar', 'message') 20 | a = adapter_for(m) 21 | assert_equal HttpAdapter, a.class 22 | end 23 | 24 | def test_adapter_for_smtp 25 | m = Message.new('fred', 'smtp:george@acme.com', 'message') 26 | a = adapter_for(m) 27 | assert_equal SmtpAdapter, a.class 28 | end 29 | 30 | end 31 | -------------------------------------------------------------------------------- /18/ex4_byname.rb: -------------------------------------------------------------------------------- 1 | 2 | def adapter_for(message) 3 | protocol = message.to.scheme.downcase 4 | adapter_name = "#{protocol.capitalize}Adapter" 5 | adapter_class = self.class.const_get(adapter_name) 6 | adapter_class.new 7 | end 8 | -------------------------------------------------------------------------------- /18/ex4_byname_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'test/unit' 4 | require 'pp' 5 | 6 | require_relative 'ex1_message' 7 | require_relative 'ex2_adapters' 8 | require_relative 'ex4_byname' 9 | 10 | class HardcodedChooserTest < Test::Unit::TestCase 11 | 12 | 13 | def test_adapter_for_file 14 | m = Message.new('fred', 'file:///foo/bar', 'message') 15 | a = adapter_for(m) 16 | assert_equal FileAdapter, a.class 17 | end 18 | 19 | def test_adapter_for_http 20 | m = Message.new('fred', 'http://www.yahoo.com:/foo/bar', 'message') 21 | a = adapter_for(m) 22 | assert_equal HttpAdapter, a.class 23 | end 24 | 25 | def test_adapter_for_smtp 26 | m = Message.new('fred', 'smtp:george@acme.com', 'message') 27 | a = adapter_for(m) 28 | assert_equal SmtpAdapter, a.class 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /18/ex5/README.txt: -------------------------------------------------------------------------------- 1 | This is the example message gateway. 2 | -------------------------------------------------------------------------------- /18/ex5/lib/adapter/file.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class FileAdapter 4 | def send_message(message) 5 | path = message.to_path 6 | path.slice!(0) 7 | File.open(path, "w") { |f| f.write(message.body) } 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /18/ex5/lib/adapter/file_adapter.rb: -------------------------------------------------------------------------------- 1 | 2 | class FileAdapter 3 | 4 | def send_message(message) 5 | # 6 | # Get the path from the url 7 | # and remove the leading '/' 8 | # 9 | to_path = message.to.path 10 | to_path.slice!(0) 11 | 12 | File.open(to_path, "w") do |f| 13 | f.write(message.body) 14 | end 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /18/ex5/lib/adapter/file_adapter.rb~: -------------------------------------------------------------------------------- 1 | 2 | class FileAdapter 3 | 4 | def send_message(message) 5 | # 6 | # Get the path from the url 7 | # and remove the leading '/' 8 | # 9 | to_path = message.to.path 10 | to_path.slice!(0) 11 | 12 | File.open(to_path, "w") do |f| 13 | f.write(message.text) 14 | end 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /18/ex5/lib/adapter/http_adapter.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'net/http' 3 | 4 | class HttpAdapter 5 | 6 | def send_message(message) 7 | 8 | Net::HTTP.start(message.to.host, message.to.port) do |http| 9 | http.post(message.to.path, message.body) 10 | end 11 | 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /18/ex5/lib/adapter/http_adapter.rb~: -------------------------------------------------------------------------------- 1 | 2 | require 'net/http' 3 | 4 | class HttpAdapter 5 | 6 | def send_message(message) 7 | 8 | Net::HTTP.start(message.to.host, message.to.port) do |http| 9 | http.post(message.to.path, message.text) 10 | end 11 | 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /18/ex5/lib/adapter/log.rb: -------------------------------------------------------------------------------- 1 | require 'pp' 2 | 3 | class LogAdapter 4 | def send_message(message) 5 | pp message 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /18/ex5/lib/adapter/smtp_adapter.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'net/smtp' 3 | 4 | class SmtpAdapter 5 | 6 | MailServerHost = 'localhost' 7 | MailServerPort = 25 8 | 9 | def send_message(message) 10 | from_address = message.from.user + '@' + message.from.host 11 | to_address = message.to.user + '@' + message.to.host 12 | 13 | email_text = "From: #{from_address}\n" 14 | email_text += "To: #{to_address}\n" 15 | email_text += "Subject: Forwarded message\n" 16 | email_text += "\n" 17 | email_text += message.body 18 | 19 | Net::SMTP.start(MailServerHost, MailServerPort) do |smtp| 20 | smtp.send_message(email_text, from_address, to_address) 21 | end 22 | 23 | end 24 | 25 | end 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /18/ex5/lib/adapter/smtp_adapter.rb~: -------------------------------------------------------------------------------- 1 | 2 | require 'net/smtp' 3 | 4 | class SmtpAdapter 5 | 6 | MailServerHost = 'localhost' 7 | MailServerPort = 25 8 | 9 | def send_message(message) 10 | from_address = message.from.user + '@' + message.from.host 11 | to_address = message.to.user + '@' + message.to.host 12 | 13 | email_text = "From: #{from_address}\n" 14 | email_text += "To: #{to_address}\n" 15 | email_text += "Subject: Forwarded message\n" 16 | email_text += "\n" 17 | email_text += message.text 18 | 19 | Net::SMTP.start(MailServerHost, MailServerPort) do |smtp| 20 | smtp.send_message(email_text, from_address, to_address) 21 | end 22 | 23 | end 24 | 25 | end 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /18/ex5/lib/adapter_scaffold.rb: -------------------------------------------------------------------------------- 1 | protocol_name = ARGV[0] 2 | class_name = protocol_name.capitalize + 'Adapter' 3 | file_name = File.join('adapter', protocol_name + '.rb') 4 | 5 | scaffolding = %Q{ 6 | 7 | class #{class_name} 8 | 9 | def send_message(message) 10 | # code to send the message 11 | end 12 | 13 | end 14 | } 15 | 16 | File.open(file_name, 'w') do |f| 17 | f.write(scaffolding) 18 | end 19 | -------------------------------------------------------------------------------- /18/ex5/lib/auth/default.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class DefaultAuthorizer 4 | 5 | def authorized?(message) 6 | false 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /18/ex5/lib/auth/russolsen_dot_com.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class RussolsenDotComAuthorizer 4 | def russ_dot_olsen_authorized?(message) 5 | true 6 | end 7 | 8 | def authorized?(message) 9 | message.body.size < 2048 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /18/ex5/lib/auth/russolsen_dot_com.rb~: -------------------------------------------------------------------------------- 1 | 2 | 3 | class RussolsenDotComAuthorizer 4 | 5 | def russ_dot_olsen_authorized?(message) 6 | true 7 | end 8 | 9 | def authorized?(message) 10 | #message.body.size < 2048 11 | false 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /18/ex5/lib/gateway0.rb: -------------------------------------------------------------------------------- 1 | require 'message' 2 | 3 | class MessageGateway 4 | def initialize 5 | load_adapters 6 | end 7 | 8 | def process_message(message) 9 | adapter = adapter_for(message) 10 | adapter.send_message(message) 11 | end 12 | 13 | def adapter_for(message) 14 | protocol = message.to.scheme 15 | adapter_class = protocol.capitalize + 'Adapter' 16 | adapter_class = self.class.const_get(adapter_class) 17 | adapter_class.new 18 | end 19 | 20 | def load_adapters 21 | lib_dir = File.dirname(__FILE__) 22 | full_pattern = File.join(lib_dir, 'adapter', '*.rb') 23 | Dir.glob(full_pattern).each {|file| require file } 24 | end 25 | end 26 | 27 | mess = Message.new('russ.olsen', 'log://russolsen.com/foo/bar', "hello out there\n") 28 | 29 | mg = MessageGateway.new 30 | mg.process_message(mess) 31 | 32 | mess = Message.new('russ.olsen', "file:///msg.out", "hello out there\n") 33 | mg.process_message(mess) 34 | -------------------------------------------------------------------------------- /18/ex5/lib/message.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../ex1_message' 2 | -------------------------------------------------------------------------------- /18/ex5/lib/msg.out: -------------------------------------------------------------------------------- 1 | hello out there 2 | -------------------------------------------------------------------------------- /18/ex5/lib/transform/from_russ_dot_olsen.erb: -------------------------------------------------------------------------------- 1 | MessageSent <%= Time.new %> 2 | 3 | <%= message.body %> 4 | -------------------------------------------------------------------------------- /18/ex5/lib/transform/to_russolsen_dot_com.erb: -------------------------------------------------------------------------------- 1 | MessageFrom: <%= message.from %> 2 | MessageTo: <%= message.to %> 3 | 4 | <%= message.body %> 5 | -------------------------------------------------------------------------------- /18/http_adapter.rb: -------------------------------------------------------------------------------- 1 | 2 | require_relative 'net/http' 3 | 4 | class HttpAdapter 5 | 6 | def send(message) 7 | 8 | Net::HTTP.start(message.to.host, message.to.port) do |http| 9 | http.post(message.to.path, message.text) 10 | end 11 | 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /18/http_server.rb: -------------------------------------------------------------------------------- 1 | 2 | require_relative 'webrick' 3 | include WEBrick 4 | 5 | s = HTTPServer.new( 6 | :Port => 7000 7 | ) 8 | 9 | class HelloServlet < HTTPServlet::AbstractServlet 10 | def do_POST(req, res) 11 | puts req 12 | end 13 | end 14 | 15 | s.mount("/", HelloServlet) 16 | 17 | trap("INT"){ s.shutdown } 18 | s.start 19 | 20 | -------------------------------------------------------------------------------- /18/msg: -------------------------------------------------------------------------------- 1 | hello -------------------------------------------------------------------------------- /19/Rakefile: -------------------------------------------------------------------------------- 1 | require '../rake_utils.rb' 2 | -------------------------------------------------------------------------------- /19/message.rb: -------------------------------------------------------------------------------- 1 | 2 | foo = nil 3 | 4 | foo.do_something() 5 | -------------------------------------------------------------------------------- /19/message2.rb: -------------------------------------------------------------------------------- 1 | class NilClass 2 | 3 | def method_missing(method_name, *args, &block) 4 | raise "You have a nil object where you didn't expect it" 5 | end 6 | 7 | end 8 | 9 | foo = nil 10 | foo.xxx 11 | foo.do_something() 12 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'test-unit' 4 | gem 'rspec' 5 | gem 'runt' 6 | #gem 'madeleine', git: 'git@github.com:ghostganz/madeleine.git' 7 | gem 'madeleine' 8 | gem 'soap4r' 9 | gem 'facets' 10 | gem 'mailfactory' 11 | gem 'pry' 12 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | Dirs = Dir['[0-9][0-9]'].sort 2 | 3 | def build_target( target ) 4 | Dirs.each do |dir| 5 | sh "cd #{dir}; rake #{target}" 6 | end 7 | end 8 | 9 | desc "Run all of the tests" 10 | task :default do 11 | build_target( :default ) 12 | end 13 | 14 | task :sniff do 15 | build_target( :sniff ) 16 | end 17 | 18 | desc "Clean up after test run." 19 | task :clean do 20 | build_target( :clean ) 21 | end 22 | -------------------------------------------------------------------------------- /rake_utils.rb: -------------------------------------------------------------------------------- 1 | task :init 2 | 3 | task :default => :init do 4 | Dir['*demo.rb'].each do |f| 5 | sh "ruby #{f}" 6 | end 7 | Dir['*_test.rb'].each do |f| 8 | sh "ruby #{f}" 9 | end 10 | end 11 | 12 | task :clean 13 | -------------------------------------------------------------------------------- /rvmrc.example: -------------------------------------------------------------------------------- 1 | rvm use 1.9.3-p327@dpir --create 2 | --------------------------------------------------------------------------------