├── 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(" Monthly/ =~ output
17 | assert /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 |
2 |
3 |
4 |
5 |
6 |
7 | thing, it's another
8 |
9 |
10 |
11 |
12 |
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 | 9ESTIE
2 | Io SKL
3 | Eo 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 | 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 |
--------------------------------------------------------------------------------