├── structures ├── comparable.rb ├── iterator.rb ├── visitor.rb ├── order_list.rb ├── stack.rb ├── cursor.rb ├── queue.rb ├── module.rb ├── abstract_object.rb ├── deque.rb ├── matrix.rb ├── priority_queue.rb ├── queue_as_linked_list.rb ├── searchable.rb ├── deque_as_linked_list.rb ├── dense_matrix.rb ├── container.rb ├── binary_heap.rb ├── queue_as_array.rb ├── deque_as_array.rb ├── array.rb ├── multi_dimensional_array.rb ├── double_linked_list.rb ├── stack_as_linked_list.rb ├── hash_table.rb ├── binary_tree.rb ├── stack_as_array.rb ├── b_tree.rb ├── binary_search_tree.rb ├── ordered_list_as_linked_list.rb ├── ordered_list_as_array.rb ├── singly_linked_list.rb ├── m_way_tree.rb ├── axioms.rb └── tree.rb ├── meta_programming ├── module_namespace_interpolation.rb ├── dynamic_dispatch.rb ├── around_alias.rb ├── ghost_methods.rb ├── class_extension_mixin.rb ├── context_probe.rb ├── class_eval.rb ├── kernel_method.rb ├── dynamic_proxies.rb ├── hook_methods.rb ├── blank_slate.rb ├── class_macros.rb ├── flattening_the_scope.rb └── dynamic_method.rb ├── .travis.yml ├── functional_programming ├── pure_functions.rb ├── lambda.rb ├── closures.rb └── partial_application_and_currying.rb ├── gotchas ├── string_or_nothing.rb ├── dont_quote_me_on_this_but.rb ├── hang_him_in_effigy.rb ├── getting_any.rb ├── ruby_can_be_surprising.rb ├── some_are_more_equal_than_others.rb ├── bang_methods_is_dangerous.rb ├── when_will_it_end_or_start.rb ├── undef_leppard.rb ├── freeze_array.rb ├── 1_is_1_and_ever_more_shall_be_so.rb ├── rescue_me_throw_a_line_ill_try_to_catch_it.rb ├── constants_arent.rb ├── precedence_of_separators.rb ├── precedence_of_unions.rb ├── precedense_of_separators.rb ├── rescue_standard_error.rb ├── an_array_of_new_gotchas.rb ├── ang_onto_yer_@.rb ├── making_a_hash_of_it.rb ├── its_twue_twue.rb ├── superman_vs_the_invisible_man.rb ├── triple_equal.rb ├── look_out_its_an_@@.rb ├── with_initialize_or_without_it.rb ├── method_missing_and_respond_to_missing.rb ├── private_data.rb ├── braces_vs_do_end.rb ├── foo_bar.rb ├── dont_be_so_sensitive.rb ├── hoisting.rb ├── to_s_vs_to_str.rb ├── private_method.rb ├── diff_proc_block_lambda.rb └── instance_eval.rb ├── test ├── algorithms_search_test.rb └── algorithms_sort_test.rb ├── patterns ├── creational │ ├── singleton.rb │ ├── factory.rb │ ├── prototype.rb │ ├── builder.rb │ └── abstract_factory.rb ├── structural │ ├── adapter.rb │ ├── facade.rb │ ├── composite.rb │ ├── decorator.rb │ ├── flyweight.rb │ └── proxy.rb └── behavioral │ ├── strategy.rb │ ├── template_method.rb │ ├── memento.rb │ ├── observer.rb │ ├── iterator.rb │ ├── visitor.rb │ ├── command.rb │ ├── interpreter.rb │ ├── mediator.rb │ ├── state.rb │ └── chain_of_responsibility.rb ├── threads ├── gil.rb ├── fibers.rb ├── mutex.rb └── rails.rb ├── algorithms ├── sort │ ├── quick.rb │ ├── insertion.rb │ ├── shell.rb │ ├── merge.rb │ ├── selection.rb │ ├── heap.rb │ └── bubble.rb └── search │ ├── binary.rb │ └── khuth-morris-pratt.rb ├── LICENSE ├── .gitignore ├── .rubocop.yml └── solid ├── dependency_inversion.rb ├── liskov_substitution.rb ├── open_close.rb ├── interface_segregation.rb └── single_responsibility.rb /structures/comparable.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /meta_programming/module_namespace_interpolation.rb: -------------------------------------------------------------------------------- 1 | type = 'baz' 2 | Foo::Bar.const_get(type.capitalize).new 3 | # => new instance of Foo::Bar::Baz 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | rvm: 4 | - 2.3.1 5 | gemfile: 6 | - Gemfile 7 | before_install: 8 | - gem install bundler 9 | script: 10 | - ruby test/* 11 | -------------------------------------------------------------------------------- /functional_programming/pure_functions.rb: -------------------------------------------------------------------------------- 1 | # You can see that this function computes the result only using its arguments. 2 | # Example of a pure function: 3 | 4 | def sum_two_numbers(a, b) 5 | a + b 6 | end 7 | -------------------------------------------------------------------------------- /functional_programming/lambda.rb: -------------------------------------------------------------------------------- 1 | # Definition 2 | 3 | say_hello = lambda { |name| puts "Hello #{ name }" } 4 | say_hello = ->(name) { puts "Hello #{ name }" } 5 | 6 | # Call 7 | 8 | say_hello.call("Mark") 9 | say_hello.("Mark") 10 | -------------------------------------------------------------------------------- /meta_programming/dynamic_dispatch.rb: -------------------------------------------------------------------------------- 1 | # Dynamic Dispatch 2 | # Allows us to send messages 3 | # object.public_send(message, *arguments) 4 | 1.public_send(:+, 2) # => 3 5 | # Even private methods 6 | # object.send(message, *arguments) 7 | 1.send(:+, 2) # => 3 8 | -------------------------------------------------------------------------------- /gotchas/string_or_nothing.rb: -------------------------------------------------------------------------------- 1 | str = "string" 2 | # => "string" 3 | 4 | str[2] 5 | # => 114 6 | # ??? ascii code! 7 | 8 | str[2..2] 9 | # => "r" 10 | # that's better! 11 | 12 | str[2,1] 13 | # => "r" 14 | # works too.... 15 | 16 | str = "string" 17 | # => "string" 18 | 19 | str[2] 20 | # => "r" 21 | # makes sense *now* 22 | -------------------------------------------------------------------------------- /gotchas/dont_quote_me_on_this_but.rb: -------------------------------------------------------------------------------- 1 | # String interpolation (including special chars like \n) fails with 'single' 2 | # quotes -- it requires "double" quotes. (Just like in most languages with string 3 | # interpolation.). To avoid: use doubles whenever practical. 4 | 5 | x = 3 6 | 7 | puts 'x = #{x}\nx' 8 | x = #{x}\nx 9 | 10 | puts "x = #{x}\nx" 11 | x = 3 12 | x 13 | -------------------------------------------------------------------------------- /structures/iterator.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Iterator < AbstractObject 9 | abstractmethod :more? 10 | 11 | abstractmethod :succ 12 | end 13 | -------------------------------------------------------------------------------- /structures/visitor.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Visitor < AbstractObject 9 | abstractmethod :visit 10 | 11 | def done? 12 | false 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /gotchas/hang_him_in_effigy.rb: -------------------------------------------------------------------------------- 1 | # Symbols != strings. Even if the same when printed. Remember which one to use 2 | # for what (args).Ideally, take either, and use what a method expects: "Be 3 | # liberal in what you accept, and conservative in what you send." (Postel's Law) 4 | 5 | str = "string" 6 | sym = :string 7 | 8 | puts str 9 | # => string 10 | 11 | puts sym 12 | # => string 13 | 14 | str == sym 15 | # => false 16 | -------------------------------------------------------------------------------- /structures/order_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class OrderedList < SearchableContainer 9 | def initialize 10 | super 11 | end 12 | 13 | abstractmethod :[] 14 | 15 | abstractmethod :findPosition 16 | end 17 | -------------------------------------------------------------------------------- /structures/stack.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Stack < Container 9 | def initialize 10 | super 11 | end 12 | 13 | abstractmethod :push 14 | 15 | abstractmethod :pop 16 | 17 | abstractmethod :top 18 | end 19 | -------------------------------------------------------------------------------- /meta_programming/around_alias.rb: -------------------------------------------------------------------------------- 1 | # Around Alias uses the `alias` keyword to store a copy of the original method under a new name, 2 | # allowing you to redefine the original method name and to delegate off to the previous method implementation 3 | 4 | class String 5 | alias orig_length length 6 | 7 | def length 8 | "Length of string '#{self}' is: #{orig_length}" 9 | end 10 | end 11 | 12 | 'abc'.length 13 | #=> "Length of string 'abc' is: 3" 14 | -------------------------------------------------------------------------------- /gotchas/getting_any.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # .any? does not mean “any elements?”! # With block: “do any make the block true?” 3 | # Without: “are any truthy?” Has implicit block: { |element| element } 4 | 5 | [].any? 6 | # => false 7 | 8 | [1].any? 9 | # => true 10 | 11 | [:foo, :bar].any? 12 | # => true 13 | 14 | # ok so far, BUT: 15 | 16 | [nil].any? 17 | # => false 18 | 19 | [false].any? 20 | # => false 21 | 22 | [false, nil].any? 23 | # => false 24 | -------------------------------------------------------------------------------- /structures/cursor.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Cursor < AbstractObject 9 | abstractmethod :datum 10 | 11 | abstractmethod :insertAfter 12 | 13 | abstractmethod :insertBefore 14 | 15 | abstractmethod :withdraw 16 | end 17 | -------------------------------------------------------------------------------- /structures/queue.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Queue < Container 9 | def initialize 10 | super 11 | end 12 | 13 | abstractmethod :enqueue 14 | 15 | abstractmethod :dequeue 16 | 17 | abstractmethod :head 18 | end 19 | -------------------------------------------------------------------------------- /meta_programming/ghost_methods.rb: -------------------------------------------------------------------------------- 1 | # Ghost Methods 2 | # Utilises `method_missing` 3 | class Hai 4 | def method_missing(method, *args) 5 | puts "You called: #{method}(#{args.join(', ')})" 6 | puts 'You also passed a block' if block_given? 7 | end 8 | end 9 | Hai.new.yolo # => You called: yolo() 10 | Hai.new.yolo 'a', 123, :c # => You called: yolo(a, 123, c) 11 | Hai.new.yolo(:a, :b, :c) { puts 'a block' } # => You called: yolo(a, b, c) 12 | # => You also passed a block 13 | -------------------------------------------------------------------------------- /structures/module.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Module 9 | def abstractmethod(symbol) 10 | module_eval <<-"end_eval" 11 | def #{symbol.id2name}(*args) 12 | raise MethodNotImplementedError 13 | end 14 | end_eval 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /gotchas/ruby_can_be_surprising.rb: -------------------------------------------------------------------------------- 1 | # Ruby can be surprising! 2 | # Though "engineered to maximize programmer happiness", with the "principle of 3 | # least surprise", Ruby still has gotchas. This presentation will proceed from 4 | # newbie trivial gotchas, to more advanced and confusing gotchas. 5 | 6 | We = 2 7 | class Fixnum 8 | def rb; self; end 9 | end 10 | 11 | We <3 .rb 12 | # => true 13 | 14 | But = 3 15 | still = 1 16 | perfect = 4 17 | 18 | But - still .rb < perfect 19 | # => true 20 | -------------------------------------------------------------------------------- /gotchas/some_are_more_equal_than_others.rb: -------------------------------------------------------------------------------- 1 | # Effectively: 2 | # == is the usual 3 | # (same value) 4 | # .eql? is value and class 5 | # (1 is Fixnum, 1.0 is Float) 6 | # .equal? is same object 7 | # It's actually much hairier; 8 | # see docs on class Object 9 | 10 | 1 == 1.0 11 | # => true 12 | 13 | 1.eql? 1.0 14 | # => false 15 | 16 | a = "foo" 17 | b = "foo" 18 | a == b 19 | # => true 20 | 21 | a.eql? b 22 | # => true 23 | 24 | a.equal? b 25 | # => false 26 | 27 | a.equal? a 28 | # => true 29 | -------------------------------------------------------------------------------- /gotchas/bang_methods_is_dangerous.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Bang marks method as dangerous. Why? Often, may modify receiver, vs 3 | # non-modding non-bang version. DO NOT RELY ON THEM RETURNING SAME VALUE AS 4 | # NON-BANG VERSION! Many return nil if no change needed! 5 | 6 | str = "foo" 7 | str.upcase 8 | # => ”FOO” 9 | 10 | str 11 | # => ”foo” 12 | 13 | str.upcase! 14 | # => ”FOO” 15 | 16 | str 17 | # => ”FOO” 18 | # Now that it’s already FOO: 19 | 20 | str.upcase! 21 | # => nil # ?! 22 | 23 | str 24 | # => ”FOO” 25 | -------------------------------------------------------------------------------- /gotchas/when_will_it_end_or_start.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # In "standard" regexps: 3 | # ^ is start and $ is end... 4 | # of the whole string. 5 | # Ruby’s regexes default to multiline, so: 6 | # ^ is start and $ is end... 7 | # of any line! 8 | # \A is start and \Z is end of the whole string. (Or \z to include any newline… 9 | # which is another gotcha!) 10 | 11 | str = "One\nTwo\nThree" 12 | 13 | str =~ /^Two$/ 14 | # => 4 15 | 16 | str =~ /\ATwo\Z/ 17 | # => nil 18 | 19 | str =~ /\AOne/ 20 | # => 0 21 | 22 | str =~ /Three\Z/ 23 | # => 8 24 | -------------------------------------------------------------------------------- /gotchas/undef_leppard.rb: -------------------------------------------------------------------------------- 1 | # Variables declared in blocks passed to iterators (e.g., times or each) are 2 | # undefined at the top of each iteration! Iterators call the block repeatedly, 3 | # so vars are out of scope again after each call. Built-in looping constructs 4 | # (e.g., while or for) are OK. (Or declare vars before block.) 5 | 6 | 3.times do |loop_num| 7 | sum ||= 0 8 | sum += 1 9 | puts sum 10 | end 11 | # 1 12 | # 1 13 | # 1 14 | 15 | for loop_num in 1..3 16 | sum ||= 0 17 | sum += 1 18 | puts sum 19 | end 20 | # 1 21 | # 2 22 | # 3 23 | -------------------------------------------------------------------------------- /gotchas/freeze_array.rb: -------------------------------------------------------------------------------- 1 | # Freezing an array (or a hash) freezes it, not the items it contains. 2 | # Strings can be modified in place. This way, you can modify a given slot in a 3 | # frozen Array of Strings. 4 | 5 | arr = ["one", "two", "three"] 6 | arr.freeze 7 | arr << "four" 8 | # RuntimeError: can't modify frozen Array 9 | 10 | arr[0] = "eno" 11 | # RuntimeError: can't modify frozen Array 12 | 13 | arr[0].object_id 14 | # => 1234567890 15 | 16 | arr[0].reverse! 17 | arr 18 | # => ["eno", "two", "three"] 19 | 20 | arr[0].object_id 21 | # => 1234567890 22 | -------------------------------------------------------------------------------- /gotchas/1_is_1_and_ever_more_shall_be_so.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Changing Fixnum to new value means new object. They can't be modified in 3 | # place! So, can’t modify a frozen Array of Fixnums. (Fixnums and Integers have 4 | # no bang-methods to demo trying with). BTW: a Fixnum's object_id is value * 2 + 1 5 | 6 | arr = [1, 2, 3, 4] 7 | arr.freeze 8 | # => [1, 2, 3, 4] 9 | 10 | arr << 5 11 | # RuntimeError: can't modify frozen Array 12 | 13 | arr[0] += 2 14 | # RuntimeError: can't modify frozen Array 15 | 16 | 1.object_id 17 | # => 3 18 | 19 | 3.object_id 20 | # => 7 21 | -------------------------------------------------------------------------------- /gotchas/rescue_me_throw_a_line_ill_try_to_catch_it.rb: -------------------------------------------------------------------------------- 1 | # In Ruby, throw and catch are NOT for exceptions! They are advanced flow control, 2 | # to exit deep nesting. Ruby uses raise and rescue for exceptions. 3 | 4 | # JAVA: 5 | 6 | try { 7 | throw new MyException("blah"); 8 | } catch(MyException e) { 9 | fix_it(); 10 | } 11 | 12 | # RUBY: 13 | index = catch(:idx) { 14 | arr.each_with_index do |v, i| 15 | throw :idx, i if v == target 16 | end 17 | -1 18 | } 19 | 20 | begin 21 | raise MyException.new "blah" 22 | rescue MyException => e 23 | fix_it 24 | end 25 | -------------------------------------------------------------------------------- /gotchas/constants_arent.rb: -------------------------------------------------------------------------------- 1 | # (Initial uppercase means constant, in Ruby.) Try to change a constant. Ooooh, 2 | # you get a WARNING! BFD. 3 | 4 | FOO = 5 5 | # => 5 6 | 7 | FOO = 7 8 | # (irb):3: warning: already initialized constant FOO 9 | # => 7 10 | 11 | FOO 12 | # => 7 13 | 14 | # Even freezing doesn't work for Fixnums. It does work for arrays (sort of) and 15 | # most other objects ... he said foreshadowingly. 16 | 17 | FOO 18 | # => 7 19 | 20 | FOO.freeze 21 | # => 7 22 | 23 | FOO += 2 24 | # (irb):5: warning: already initialized constant FOO 25 | # => 9 26 | 27 | FOO 28 | # => 9 29 | -------------------------------------------------------------------------------- /gotchas/precedence_of_separators.rb: -------------------------------------------------------------------------------- 1 | # || has higher precedence 2 | # than =, so 3 | # x = false || true 4 | # means 5 | # x = (false || true) 6 | # or has lower precedence, so 7 | # x = false or true 8 | # means 9 | # (x = false) or true 10 | # Also, && is higher than ||, but and and or are equal, so they are evaluated 11 | # left-to-right! 12 | 13 | x = false || true 14 | # => true 15 | 16 | x 17 | # => true 18 | 19 | # OK so far, but: 20 | 21 | x = false or true 22 | # => true 23 | 24 | x 25 | # => false 26 | 27 | # Return value is true but variable is false! Why the mismatch?! 28 | -------------------------------------------------------------------------------- /gotchas/precedence_of_unions.rb: -------------------------------------------------------------------------------- 1 | # && has higher precedence 2 | # than =, so 3 | # x = true && false 4 | # means 5 | # x = (true && false) 6 | # and has lower precedence, so 7 | # x = true and false 8 | # means 9 | # (x = true) and false 10 | # Ruby Style Guide: Use && / || for boolean expressions, [use] and / or for 11 | # control flow. 12 | 13 | x = true && false 14 | # => false 15 | 16 | x 17 | # => false 18 | 19 | # OK so far, but: 20 | 21 | x = true and false 22 | # => false 23 | 24 | x 25 | # => true 26 | 27 | # Return value is false but variable is true! Why the mismatch?! 28 | -------------------------------------------------------------------------------- /gotchas/precedense_of_separators.rb: -------------------------------------------------------------------------------- 1 | # || has higher precedence 2 | # than =, so 3 | # x = false || true 4 | # means 5 | # x = (false || true) 6 | # or has lower precedence, so 7 | # x = false or true 8 | # means 9 | # (x = false) or true 10 | # Also, && is higher than ||, but and and or are equal, so they are evaluated 11 | # left-to-right! 12 | 13 | x = false || true 14 | # => true 15 | 16 | x 17 | # => true 18 | 19 | # OK so far, but: 20 | 21 | x = false or true 22 | # => true 23 | 24 | x 25 | # => false 26 | 27 | # Return value is true but variable is false! Why the mismatch?! 28 | -------------------------------------------------------------------------------- /meta_programming/class_extension_mixin.rb: -------------------------------------------------------------------------------- 1 | # Class Extension Mixin allows you to both `include` and `extend` a class 2 | module MyMixin 3 | def self.included(base) # Hook Method 4 | base.extend(ClassMethods) 5 | end 6 | 7 | def a 8 | puts "I'm A (an instance method)" 9 | end 10 | 11 | module ClassMethods # "ClassMethods" is a recognised naming pattern 12 | def x 13 | puts "I'm X (a class method)" 14 | end 15 | end 16 | end 17 | 18 | class Foo 19 | include MyMixin 20 | end 21 | 22 | Foo.x # => I'm X (a class method) 23 | Foo.new.a # => I'm A (an instance method) 24 | -------------------------------------------------------------------------------- /gotchas/rescue_standard_error.rb: -------------------------------------------------------------------------------- 1 | # Don't rescue Exception, rescue StandardError 2 | # Before Explicitly rescuing Exception will rescue even not normally recoverable 3 | # errors such as SyntaxError, LoadError, and Interrupt. 4 | 5 | begin 6 | foo 7 | rescue Exception => e 8 | logger.warn "Unable to foo, will ignore: #{e}" 9 | end 10 | 11 | # Refactor 12 | # If you omit the Exception type qualifier, then Ruby will catch only 13 | # StandardError, which is probably what you want: 14 | 15 | begin 16 | foo 17 | rescue StandardError => e 18 | logger.warn "Unable to foo, will ignore: #{e}" 19 | end 20 | -------------------------------------------------------------------------------- /gotchas/an_array_of_new_gotchas.rb: -------------------------------------------------------------------------------- 1 | # Default value given as object is same object for each slot! Mutating one 2 | # mutates default for all. Initial value given as block gets evaluated separately 3 | # for each slot. Use this to create new vars for each. 4 | 5 | class Person 6 | attr_accessor :name 7 | end 8 | 9 | people = Array.new(3, Person.new) 10 | people[0].name = "Alice" 11 | people[1].name = "Bob" 12 | people[0].name 13 | # => "Bob" # should have been "Alice"! 14 | 15 | people = Array.new(3) { 16 | Person.new } 17 | people[0].name = "Alice" 18 | people[1].name = "Bob" 19 | people[0].name 20 | # => "Alice" 21 | -------------------------------------------------------------------------------- /test/algorithms_search_test.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | 3 | require_relative '../algorithms/search/binary' 4 | require_relative '../algorithms/search/khuth-morris-pratt' 5 | 6 | class AlgorithmsTest < Test::Unit::TestCase 7 | IN_ARRAY = [1,2,3] 8 | IN_STRING = "ABC ABCDAB ABCDABCDABDE" 9 | 10 | def test_binary_search 11 | assert_equal(1, IN_ARRAY.binary_search(1)) 12 | assert_nil(nil, IN_ARRAY.binary_search(4)) 13 | end 14 | 15 | def test_khuth_morris_pratt_search 16 | assert_equal(15, IN_STRING.kmp_search("ABCDABD")) 17 | assert_nil(nil, IN_STRING.kmp_search("ABCDEF")) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /gotchas/ang_onto_yer_@.rb: -------------------------------------------------------------------------------- 1 | # Naked value becomes a temporary local variable! Solution: remember the @! (Or 2 | # "self.". Or use attr_writer too, or an attr_accessor.) Gets people from 3 | # Java/C++, not so much Python (which needs "self." too). "You keep on using that 4 | # variable. I don't think it means what you think it means.". Not Inigo Montoya. 5 | 6 | class Foo 7 | attr_reader :value 8 | 9 | def initialize(v) 10 | value = v 11 | end 12 | 13 | def set_val(v) 14 | @value = v 15 | end 16 | end 17 | 18 | f = Foo.new(3) 19 | f.value 20 | # => nil # not 3?! 21 | 22 | f.set_val 5 23 | # => 5 24 | 25 | f.value 26 | # => 5 27 | -------------------------------------------------------------------------------- /gotchas/making_a_hash_of_it.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Mostly same problem (and solution) as Arrays. MORE GOTCHAS: creates new object 3 | # on any access to empty slot! May create excessive number of new objects; ruins 4 | # checking “real” contents or count (nil-checking, .size, etc.). 5 | 6 | langs = Hash.new [] 7 | langs[:jane] << "Java" 8 | langs[:rachel] << "Ruby" 9 | 10 | langs[:jane] 11 | # => ["Java", "Ruby"] 12 | 13 | langs[:rachel] 14 | # => ["Java", "Ruby"] 15 | 16 | langs = Hash.new { |h, k| h[k] = [] } 17 | langs[:jane] << "Java" 18 | langs[:rachel] << "Ruby" 19 | 20 | langs[:jane] 21 | # => ["Java"] 22 | 23 | langs[:rachel] 24 | # => ["Ruby"] 25 | -------------------------------------------------------------------------------- /gotchas/its_twue_twue.rb: -------------------------------------------------------------------------------- 1 | # Only two things are false (falsey): false, and nil. Everything else is true 2 | # (truthy), even 0 (false in C), "" (false in JS), [], etc. (Trips up people 3 | # from C, JS, etc. where some of these are false.) 4 | 5 | true ? "true" : "false" 6 | # => "true" 7 | 8 | false ? "true" : "false" 9 | # => "false" 10 | 11 | nil ? "true" : "false" 12 | # => "false" 13 | 14 | 1 ? "true" : "false" 15 | # => "true" 16 | 17 | 0 ? "true" : "false" 18 | # => "true" 19 | 20 | "false" ? "true" : "false" 21 | # => "true" 22 | 23 | "" ? "true" : "false" 24 | # => "true" 25 | 26 | [] ? "true" : "false" 27 | # => "true" 28 | -------------------------------------------------------------------------------- /gotchas/superman_vs_the_invisible_man.rb: -------------------------------------------------------------------------------- 1 | # super with no arg list sends what caller got 2 | # super with explicit args sends those args 3 | # to send NO args, use empty parens: super() 4 | 5 | class Parent 6 | def add *args 7 | args.inject :+ 8 | end 9 | end 10 | 11 | class Child2 < Parent 12 | def add arg1, arg2 13 | super arg1, arg2 14 | end 15 | end 16 | 17 | class Child4 < Parent 18 | def add a1, a2, a3, a4 19 | super # no args! 20 | end 21 | end 22 | 23 | Child2.new.add 1, 2, 3, 5 24 | # ArgumentError: wrong number of arguments (4 for 2) 25 | 26 | Child2.new.add 1, 2 27 | # => 3 28 | 29 | Child4.new.add 1, 2, 3, 5 30 | # => 11 31 | -------------------------------------------------------------------------------- /meta_programming/context_probe.rb: -------------------------------------------------------------------------------- 1 | # Context Probe 2 | # Execute a code block in the context of another object using `instance_eval` 3 | class Foo 4 | def initialize 5 | @z = 1 6 | end 7 | end 8 | foo = Foo.new 9 | foo.instance_eval do 10 | puts self # => # 11 | puts @z # => 1 12 | end 13 | new_value = 2 14 | foo.instance_eval { @z = new_value } 15 | foo.instance_eval { puts @z } # => 2 16 | 17 | # There is also `instance_exec` which works the same way but allows passing arguments to the block 18 | class Foo 19 | def initialize 20 | @x = 1 21 | @y = 2 22 | end 23 | end 24 | Foo.new.instance_exec(3) { |arg| (@x + @y) * arg } # => 9 25 | -------------------------------------------------------------------------------- /gotchas/triple_equal.rb: -------------------------------------------------------------------------------- 1 | # Effectively: 2 | # === is "case equality", as in case statements. A better name (IMHO) might be 3 | # ".describes?", or overload ".includes?"! Again, it's actually much hairier; see 4 | # the docs on class Object. Gets people from languages where === is either object 5 | # identity or same value and class. 6 | 7 | 1 === 1 8 | # => true 9 | 10 | Fixnum === 1 11 | # => true 12 | 13 | 1 === Fixnum 14 | # => false 15 | 16 | Class === Class 17 | Object === Object 18 | Class === Object 19 | Object === Class 20 | # => all true 21 | 22 | Fixnum === Fixnum 23 | # => false 24 | 25 | (1..3) === 2 26 | # => true 27 | 28 | 2 === (1..3) 29 | # => false 30 | -------------------------------------------------------------------------------- /meta_programming/class_eval.rb: -------------------------------------------------------------------------------- 1 | # Evaluate a block in the context of a class 2 | # Similar to re-opening a class but more flexible in that it 3 | # works on any variable that references a class, where as re-opening 4 | # a class requires defining a constant. 5 | 6 | # Classic class re-opening style 7 | class String 8 | def m 9 | puts 'hello!' 10 | end 11 | end 12 | 13 | # Class eval style 14 | # The extra code is used to make the example a bit more re-usable/abstracted 15 | def add_method_to_class(the_class) 16 | the_class.class_eval do 17 | def m 18 | puts 'hello!' 19 | end 20 | end 21 | end 22 | 23 | add_method_to_class String 24 | 'abc'.m # => hello! 25 | -------------------------------------------------------------------------------- /patterns/creational/singleton.rb: -------------------------------------------------------------------------------- 1 | # The singleton pattern is used to ensure that there is only one instance of a 2 | # class and provides global access to that instance. This pattern is useful when 3 | # you want one instance of a class and many different objects need to access it, 4 | # rather than pass the object around, we can make the object global. 5 | 6 | class Singleton 7 | def self.new 8 | @instance ||= super 9 | end 10 | end 11 | 12 | # Other example 13 | require 'singleton' 14 | 15 | class OtherSingleton 16 | include Singleton 17 | 18 | # OtherSingleton.new is not allowed 19 | end 20 | 21 | # Usage 22 | s1 = Singleton.new 23 | s2 = OtherSingleton.instance 24 | -------------------------------------------------------------------------------- /structures/abstract_object.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | class AbstractObject < ::Object 8 | def initialize 9 | super 10 | end 11 | 12 | include ::Comparable 13 | 14 | def <=>(obj) 15 | if is_a?(obj.type) 16 | compareTo(obj) 17 | elsif obj.is_a?(type) 18 | -obj.compareTo(self) 19 | else 20 | type.name <=> obj.type.name 21 | end 22 | end 23 | 24 | abstractmethod :compareTo 25 | 26 | protected :compareTo 27 | end 28 | -------------------------------------------------------------------------------- /threads/gil.rb: -------------------------------------------------------------------------------- 1 | # This simple fact is what makes threads so powerful, and also what makes them 2 | # difficult to work with. I've already given you an idea of why threads are 3 | # good; here's a simple program to illustrate their difficulty. 4 | 5 | shared_array = [] 6 | 7 | 10.times.map do 8 | Thread.new do 9 | 1000.times do 10 | shared_array << nil 11 | end 12 | end 13 | end.each(&:join) 14 | 15 | # Here you can see that we have 10 * 10000 elements in array 16 | 17 | puts shared_array.size 18 | 19 | # Note that different ruby can show different result 20 | # GIL exist only in MRI ruby 21 | 22 | # $ ruby => 10000 23 | # $ jruby => 7521 24 | # $ rbx => 8541 25 | -------------------------------------------------------------------------------- /structures/deque.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | module DequeMethods 9 | abstractmethod :enqueueHead 10 | 11 | abstractmethod :dequeueHead 12 | 13 | abstractmethod :head 14 | 15 | abstractmethod :enqueueTail 16 | 17 | abstractmethod :dequeueTail 18 | 19 | abstractmethod :tail 20 | end 21 | 22 | class Deque < Queue 23 | def initialize 24 | super 25 | end 26 | 27 | alias queueHead head 28 | include DequeMethods 29 | alias head queueHead 30 | end 31 | -------------------------------------------------------------------------------- /gotchas/look_out_its_an_@@.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # What the fillintheblank? We didn't change Parent’s @@value before checking it, 3 | # nor Child’s at all! Or did we? @@ variables are shared with subclasses -- not 4 | # just that they exist, but the variables themselves! Declaring Child’s @@value 5 | # changed Parent’s, and inc’ing Parent’s changed Child’s. 6 | 7 | class Parent 8 | @@value = 6 9 | 10 | def self.value 11 | @@value 12 | end 13 | 14 | def self.inc_value 15 | @@value += 1 16 | end 17 | end 18 | 19 | class Child < Parent 20 | @@value = 42 21 | end 22 | 23 | Parent.value 24 | # => 42 # wtf? 25 | 26 | Parent.inc_value 27 | Child.value 28 | # => 43 # wtf?! 29 | -------------------------------------------------------------------------------- /structures/matrix.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Matrix 9 | def initialize(numberOfRows, numberOfColumns) 10 | assert { numberOfRows >= 0 } 11 | assert { numberOfColumns >= 0 } 12 | 13 | @numberOfRows = numberOfRows 14 | @numberOfColumns = numberOfColumns 15 | end 16 | 17 | attr_reader :numberOfRows 18 | 19 | attr_reader :numberOfColumns 20 | 21 | abstractmethod :+ 22 | 23 | abstractmethod :* 24 | 25 | abstractmethod :transpose 26 | end 27 | -------------------------------------------------------------------------------- /gotchas/with_initialize_or_without_it.rb: -------------------------------------------------------------------------------- 1 | # Parent's initialize runs automagically only if child has none. Else, parent's 2 | # must be called to run. 3 | 4 | class Parent 5 | def initialize 6 | puts "Parent init" 7 | end 8 | end 9 | 10 | class NoInitChild < Parent 11 | end 12 | 13 | NoInitChild.new 14 | # Parent init 15 | 16 | class NormalChild < Parent 17 | def initialize 18 | puts "NormalChild init" 19 | end 20 | end 21 | 22 | NormalChild.new 23 | # "NormalChild init" 24 | 25 | class SuperChild < Parent 26 | def initialize 27 | puts "SuperChild" 28 | super 29 | puts "init" 30 | end 31 | end 32 | 33 | SuperChild.new 34 | # SuperChild 35 | # Parent init 36 | # init 37 | -------------------------------------------------------------------------------- /algorithms/sort/quick.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Shell sort: Similar approach as insertion sort but slightly better. 3 | # Requirements: Needs to be able to compare elements with <=>, and the [] []= 4 | # methods should be implemented for the container. 5 | # Time Complexity: О(n^2) 6 | # Space Complexity: О(n) total, O(1) auxiliary 7 | # Stable: Yes 8 | # 9 | # [5, 4, 3, 1, 2].quick_sort! => [1, 2, 3, 4, 5] 10 | 11 | class Array 12 | def swap(first, second) 13 | self[first], self[second] = self[second], self[first] 14 | self 15 | end 16 | 17 | def quick_sort! 18 | h, *t = self 19 | h ? t.partition { |e| e < h }.inject { |l, r| l.quick_sort! + [h] + r.quick_sort! } : [] 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /algorithms/sort/insertion.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Insertion sort: Elements are inserted sequentially into the right position. 3 | # Requirements: Needs to be able to compare elements with <=>, and the [] []= 4 | # methods should be implemented for the container. Time Complexity: О(n^2) 5 | # Space Complexity: О(n) total, O(1) auxiliary 6 | # Stable: Yes 7 | # 8 | # [5, 4, 3, 1, 2].insertion_sort! => [1, 2, 3, 4, 5] 9 | 10 | class Array 11 | def insertion_sort! 12 | return self if length < 2 13 | 14 | 1.upto(length - 1) do |i| 15 | value = self[i] 16 | j = i - 1 17 | while j >= 0 && self[j] > value 18 | self[j + 1] = self[j] 19 | j -= 1 20 | end 21 | self[j + 1] = value 22 | end 23 | 24 | self 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /meta_programming/kernel_method.rb: -------------------------------------------------------------------------------- 1 | # Kernel Method 2 | # Add a method that gives the illusion it's a language keyword 3 | # But really it's just added to the `Kernel` module which all other objects inherit from. 4 | # At the top level of a Ruby program `self` is == `main`. 5 | # `self.class` == `Object` and the `Kernel` sits above it in the hierarchy. 6 | # You can see this by running the following code: 7 | class Foo; end 8 | Foo.ancestors # => [Foo, Object, Kernel, BasicObject] 9 | 10 | # So we can see we can add what looks to be a language provided feature like so: 11 | module Kernel 12 | def foobar 13 | puts "I'm not a language keyword, I'm just a fake" 14 | end 15 | end 16 | 17 | # Now from any where in our program we can call 18 | foobar # => I'm not a language keyword, I'm just a fake -------------------------------------------------------------------------------- /structures/priority_queue.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | module PriorityQueueMethods 9 | abstractmethod :enqueue 10 | 11 | abstractmethod :min 12 | 13 | abstractmethod :dequeueMin 14 | end 15 | 16 | class PriorityQueue < Container 17 | def initialize 18 | super 19 | end 20 | 21 | include PriorityQueueMethods 22 | end 23 | 24 | module MergeablePriorityQueueMethods 25 | abstractmethod :merge! 26 | end 27 | 28 | class MergeablePriorityQueue < PriorityQueue 29 | def initialize 30 | super 31 | end 32 | 33 | include MergeablePriorityQueueMethods 34 | end 35 | -------------------------------------------------------------------------------- /patterns/structural/adapter.rb: -------------------------------------------------------------------------------- 1 | # An adapter helps two incompatible interfaces to work together. This is the 2 | # real world definition for an adapter. Interfaces may be incompatible but the 3 | # inner functionality should suit the need. The Adapter design pattern allows 4 | # otherwise incompatible classes to work together by converting the interface 5 | # of one class into an interface expected by the clients. 6 | 7 | class Adaptee 8 | def walk 9 | 'walk_as_adaptee' 10 | end 11 | end 12 | 13 | class Adaptor 14 | attr_reader :adaptee 15 | 16 | def initialize 17 | @adaptee = Adaptee.new 18 | end 19 | 20 | def walk 21 | adaptee.walk + ' and_sing_song_as_adaptor' 22 | end 23 | end 24 | 25 | # Usage 26 | 27 | adaptor = Adaptor.new 28 | adaptor.walk # => walk_as_adaptor and_sing_song_as_adaptor 29 | -------------------------------------------------------------------------------- /structures/queue_as_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class QueueAsLinkedList < Queue 9 | def initialize 10 | super 11 | @list = SinglyLinkedList.new 12 | end 13 | 14 | def purge 15 | @list.purge 16 | super 17 | end 18 | 19 | def head 20 | raise ContainerEmpty if @count == 0 21 | @list.first 22 | end 23 | 24 | def enqueue(obj) 25 | @list.append(obj) 26 | @count += 1 27 | end 28 | 29 | def dequeue 30 | raise ContainerEmpty if @count == 0 31 | result = @list.first 32 | @list.extract(result) 33 | @count -= 1 34 | result 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /algorithms/sort/shell.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Shell sort: Similar approach as insertion sort but slightly better. 3 | # Requirements: Needs to be able to compare elements with <=>, and the [] []= 4 | # methods should be implemented for the container. 5 | # Time Complexity: О(n^2) 6 | # Space Complexity: О(n) total, O(1) auxiliary 7 | # Stable: Yes 8 | # 9 | # [5, 4, 3, 1, 2].shell_sort! => [1, 2, 3, 4, 5] 10 | 11 | class Array 12 | def shell_sort! 13 | inc = length / 2 14 | 15 | while inc != 0 16 | inc.step(length - 1) do |i| 17 | el = self[i] 18 | while i >= inc && self[i - inc] > el 19 | self[i] = self[i - inc] 20 | i -= inc 21 | end 22 | self[i] = el 23 | end 24 | inc = (inc == 2 ? 1 : (inc * 5.0 / 11).to_i) 25 | end 26 | 27 | self 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /patterns/behavioral/strategy.rb: -------------------------------------------------------------------------------- 1 | # Define a family of algorithms, encapsulate each one, and make them 2 | # interchangeable. Strategy lets the algorithm vary independently from clients that use it. 3 | 4 | # Strategies 5 | class StrategyOne 6 | def use 7 | puts 'Strategy one' 8 | end 9 | end 10 | 11 | class StrategyTwo 12 | def use 13 | puts 'Strategy two' 14 | end 15 | end 16 | 17 | class StrategyThree 18 | def use 19 | puts 'Strategy three' 20 | end 21 | end 22 | 23 | # Client 24 | class Context 25 | @@default_strategy = StrategyOne.new 26 | 27 | def set_strategy(strategy) 28 | @strategy = strategy 29 | end 30 | 31 | def use 32 | @strategy.use 33 | end 34 | end 35 | 36 | # Usage 37 | context = Context.new 38 | context.use # => "Strategy one" 39 | 40 | context.set_strategy StrategyTwo.new 41 | context.use # => "Strategy two" 42 | -------------------------------------------------------------------------------- /structures/searchable.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | module SearchableContainerMethods 9 | abstractmethod :member? 10 | 11 | abstractmethod :insert 12 | 13 | abstractmethod :withdraw 14 | 15 | abstractmethod :find 16 | end 17 | 18 | class SearchableContainer < Container 19 | include SearchableContainerMethods 20 | 21 | def initialize 22 | super 23 | end 24 | end 25 | 26 | module SearchTreeMethods 27 | abstractmethod :min 28 | 29 | abstractmethod :max 30 | end 31 | 32 | class SearchTree < Tree 33 | def initialize 34 | super 35 | end 36 | 37 | include SearchableContainerMethods 38 | include SearchTreeMethods 39 | end 40 | -------------------------------------------------------------------------------- /algorithms/search/binary.rb: -------------------------------------------------------------------------------- 1 | # Binary Search: This search finds an item in log(n) time provided that the 2 | # container is already sorted. The method returns the item if it is found, or nil 3 | # if it is not. If there are duplicates, the first one found is returned, and 4 | # this is not guaranteed to be the smallest or largest item. 5 | # 6 | # Complexity: O(lg N) 7 | # 8 | # [1, 2, 3].binary_search(1) #=> 1 9 | # [1, 2, 3].binary_search(4) #=> nil 10 | 11 | class Array 12 | def binary_search(item) 13 | return nil if item.nil? 14 | 15 | low = 0 16 | high = self.size - 1 17 | 18 | while low <= high 19 | mid = (low + high) / 2 20 | val = self[mid] 21 | if val > item 22 | high = mid - 1 23 | elsif val < item 24 | low = mid + 1 25 | else 26 | return val 27 | end 28 | end 29 | 30 | nil 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /threads/fibers.rb: -------------------------------------------------------------------------------- 1 | # Fibers are primitives for implementing light weight cooperative concurrency in 2 | # Ruby. Basically they are a means of creating code blocks that can be paused 3 | # and resumed, much like threads. The main difference is that they are never 4 | # preempted and that the scheduling must be done by the programmer and not the VM. 5 | 6 | require 'fiber' 7 | 8 | f = Fiber.new do |value| 9 | first = value 10 | puts "first call fiber with args: #{first ? first : 'not passed'}" 11 | second = Fiber.yield 12 | puts "second call fiber with args: #{second ? second : 'not passed'}" 13 | other = Fiber.yield 14 | puts "First: #{first}, Second: #{second}, Other: #{other ? other : 'not passed'}" 15 | puts 'Last call' 16 | end 17 | 18 | f.resume('First argument') 19 | f.resume('Second argument') 20 | f.resume 21 | # f.resume call error if try call one more time fiber is dead 22 | -------------------------------------------------------------------------------- /algorithms/sort/merge.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Mergesort: A stable divide-and-conquer sort that sorts small chunks of the 3 | # container and then merges them together. Returns an array of the sorted 4 | # elements. Requirements: Container should implement [] 5 | # Time Complexity: О(n log n) average and worst-case 6 | # Space Complexity: О(n) auxiliary 7 | # Stable: Yes 8 | # 9 | # [5, 4, 3, 1, 2].merge_sort! => [1, 2, 3, 4, 5] 10 | 11 | class Array 12 | def merge_sort! 13 | return self if self.size <= 1 14 | mid = self.size / 2 15 | left = self[0...mid] 16 | right = self[mid...self.size] 17 | merge(left.merge_sort!, right.merge_sort!) 18 | end 19 | 20 | def merge(left, right) 21 | sorted = [] 22 | until left.empty? || right.empty? 23 | sorted << (left.first <= right.first ? left.shift : right.shift) 24 | end 25 | sorted + left + right 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /structures/deque_as_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class DequeAsLinkedList < QueueAsLinkedList 9 | alias queueHead head 10 | 11 | include DequeMethods 12 | 13 | def initialize 14 | super 15 | end 16 | 17 | alias head queueHead 18 | 19 | def enqueueHead(obj) 20 | @list.prepend(obj) 21 | @count += 1 22 | end 23 | 24 | alias dequeueHead dequeue 25 | 26 | def tail 27 | raise ContainerEmpty if @count == 0 28 | @list.last 29 | end 30 | 31 | alias enqueueTail enqueue 32 | 33 | def dequeueTail 34 | raise ContainerEmpty if @count == 0 35 | result = @list.last 36 | @list.extract(result) 37 | @count -= 1 38 | result 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /meta_programming/dynamic_proxies.rb: -------------------------------------------------------------------------------- 1 | # Dynamic Proxies 2 | # Catching "Ghost Methods" and forwarding them onto another method 3 | # Whilst possibly adding logic around the call. 4 | # 5 | # For example, 6 | # You can provide imaginary methods by utilising `method_missing` to parse 7 | # the incoming message (e.g. `get_name`, `get_age`) and to delegate off to 8 | # another method such as `get(:data_type)` where `:data_type` is `:name` or `:age`. 9 | def method_missing(message, *args, &block) 10 | return get(Regexp.last_match(1).to_sym, *args, &block) if message.to_s.match?(/^get_(.*)/) 11 | super # if we don't find a match then we'll call the top level `BasicObject#method_missing` 12 | end 13 | 14 | # If (after analysis) you discover a performance issue with using `method_missing` 15 | # you can utilise the "Dynamic Method" technique to create a real method after 16 | # the message has been received by `method_missing` the first time. 17 | -------------------------------------------------------------------------------- /patterns/creational/factory.rb: -------------------------------------------------------------------------------- 1 | # Factory pattern is one of most used design pattern in Object oriented design. 2 | # This type of design pattern comes under creational pattern as this pattern 3 | # provides one of the best ways to create an object. In Factory pattern, we 4 | # create object without exposing the creation logic to the client and refer to 5 | # newly created object using a common interface. 6 | 7 | # Classes witch are used in factory 8 | class Rectangle 9 | # Implementation 10 | end 11 | 12 | class Square 13 | # Implementation 14 | end 15 | 16 | class Circle 17 | # Implementation 18 | end 19 | 20 | # Factory 21 | class ShapeFactory 22 | def get_shape(type) 23 | case type 24 | when :rectangle then Rectangle.new 25 | when :square then Square.new 26 | when :circle then Circle.new 27 | end 28 | end 29 | end 30 | 31 | # Usage 32 | 33 | shape_factory = ShapeFactory.new 34 | square = shape_factory.get_shape :square 35 | -------------------------------------------------------------------------------- /meta_programming/hook_methods.rb: -------------------------------------------------------------------------------- 1 | # Hook Methods are provided by the Ruby language and let you know about certain events 2 | # such as when a class inherits from another class or when a method has been added to an object. 3 | class String 4 | def self.inherited(subclass) 5 | puts "#{self} was inherited by #{subclass}" 6 | end 7 | end 8 | class MyString < String; end # => String was inherited by MyString 9 | 10 | # ## Method-related hooks 11 | # 12 | # method_missing 13 | # method_added 14 | # singleton_method_added 15 | # method_removed 16 | # singleton_method_removed 17 | # method_undefined 18 | # singleton_method_undefined 19 | # 20 | # ## Class & Module Hooks 21 | # 22 | # inherited 23 | # append_features 24 | # included 25 | # extend_object 26 | # extended 27 | # initialize_copy 28 | # const_missing 29 | # 30 | # ## Marshalling Hooks 31 | # 32 | # marshal_dump 33 | # marshal_load 34 | # 35 | # ## Coercion Hooks 36 | # 37 | # coerce 38 | # induced_from 39 | # to_xxx 40 | -------------------------------------------------------------------------------- /structures/dense_matrix.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class DenseMatrix < Matrix 9 | def initialize(rows, cols) 10 | super 11 | @array = MultiDimensionalArray.new(rows, cols) 12 | end 13 | 14 | def [](i, j) 15 | @array[i, j] 16 | end 17 | 18 | def []=(i, j, value) 19 | @array[i, j] = value 20 | end 21 | 22 | def *(mat) 23 | assert { numberOfColumns == mat.numberOfRows } 24 | result = DenseMatrix.new( 25 | numberOfRows, mat.numberOfColumns 26 | ) 27 | for i in 0...numberOfRows 28 | for j in 0...mat.numberOfColumns 29 | sum = 0 30 | for k in 0...numberOfColumns 31 | sum += self[i, k] * mat[k, j] 32 | end 33 | result[i, j] = sum 34 | end 35 | end 36 | 37 | result 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /patterns/behavioral/template_method.rb: -------------------------------------------------------------------------------- 1 | # Define the skeleton of an algorithm in an operation, deferring some steps to 2 | # subclasses. Template method lets subclasses redefine certain steps of an 3 | # algorithm without changing the algorithm's structure. 4 | 5 | module Template 6 | # mandatory_methods = ["tagname", "content"] 7 | # optional_methods = ["font_size", "background_color"] 8 | 9 | def generate 10 | string = "<#{tagname}" 11 | styles = '' 12 | styles += "font-size:#{font_size};" if respond_to? :font_size 13 | styles += "background-color:#{background_color};" if respond_to? :background_color 14 | string += " style='#{styles}'" unless styles.empty? 15 | string += ">#{content}" 16 | end 17 | end 18 | 19 | class Body 20 | def tagname 21 | 'body' 22 | end 23 | 24 | def content 25 | 'hello' 26 | end 27 | 28 | def font_size 29 | '18pt' 30 | end 31 | 32 | include Template 33 | end 34 | 35 | # Usage 36 | body = Body.new 37 | body.generate 38 | -------------------------------------------------------------------------------- /structures/container.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Container < AbstractObject 9 | include ::Enumerable 10 | 11 | def initialize 12 | super 13 | @count = 0 14 | end 15 | 16 | attr_reader :count 17 | 18 | def purge 19 | @count = 0 20 | end 21 | 22 | def empty? 23 | count == 0 24 | end 25 | 26 | def full? 27 | false 28 | end 29 | 30 | abstractmethod :iter 31 | 32 | def each 33 | i = iter 34 | yield i.succ while i.more? 35 | end 36 | 37 | def to_s 38 | s = '' 39 | each do |obj| 40 | s << ', ' unless s.empty? 41 | s << obj.to_s 42 | end 43 | type.name + '{' + s + '}' 44 | end 45 | 46 | def accept(visitor) 47 | assert { visitor.is_a?(Visitor) } 48 | each do |obj| 49 | break if visitor.done? 50 | visitor.visit(obj) 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /functional_programming/closures.rb: -------------------------------------------------------------------------------- 1 | # Lambda's also enforce a closure and so are able to keep their context across 2 | # objects, as demonstrated below: 3 | 4 | require 'json' 5 | 6 | class Bar 7 | attr_reader :l 8 | 9 | def initialize(h = {}) 10 | @l = h[:l] || ->(_) { p "no-op"; false } 11 | end 12 | 13 | def dothing 14 | result = l.call("Mark") 15 | p "result = #{ result }" 16 | end 17 | end 18 | 19 | class Foo 20 | def initialize 21 | @h = { 22 | l: ->(name) { p "hello #{ name }"; foo_test } 23 | } 24 | @bar = Bar.new(@h) # remove @h to test for defensive behaviour 25 | end 26 | 27 | def start 28 | @bar.dothing 29 | end 30 | 31 | private 32 | 33 | def foo_test 34 | p "I'm internal to Foo class" 35 | raise ::JSON::ParserError 36 | true # never reached due to above line triggering an error 37 | rescue ::JSON::ParserError 38 | p "caught an error" 39 | false 40 | end 41 | end 42 | 43 | foo = Foo.new 44 | foo.start 45 | 46 | # => "hello Mark" 47 | # => "I'm internal to Foo class" 48 | # => "caught an error" 49 | # => "result = false" 50 | -------------------------------------------------------------------------------- /patterns/structural/facade.rb: -------------------------------------------------------------------------------- 1 | # The goal of the Facade Pattern is to provide a unified interface to a set of 2 | # interfaces in a subsystem. This means you'd just have some object that can 3 | # send back other objects. 4 | 5 | # Complex Parts 6 | class CPU 7 | def freeze 8 | # Implementation 9 | end 10 | 11 | def jump(position) 12 | # Implementation 13 | end 14 | 15 | def execute 16 | # Implementation 17 | end 18 | end 19 | 20 | class Memory 21 | def load(position, data) 22 | # Implementation 23 | end 24 | end 25 | 26 | class HardDrive 27 | def read(lba, size) 28 | # Implementation 29 | end 30 | end 31 | 32 | # Facade 33 | class ComputerFacade 34 | attr_reader :processor, :ram, :hd 35 | 36 | def initialize 37 | @processor = CPU.new 38 | @ram = Memory.new 39 | @hd = HardDrive.new 40 | end 41 | 42 | def start 43 | processor.freeze 44 | processor.jump(BOOT_ADDRESS) 45 | processor.execute 46 | ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE)) 47 | end 48 | end 49 | 50 | # Usage 51 | computer_facade = ComputerFacade.new 52 | computer_facade.start 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Marat Khusnetdinov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /structures/binary_heap.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class BinaryHeap < PriorityQueue 9 | def enqueue(obj) 10 | raise ContainerFull if @count == @array.length 11 | @count += 1 12 | i = @count 13 | while (i > 1) && (@array[i / 2] > obj) 14 | @array[i] = @array[i / 2] 15 | i /= 2 16 | end 17 | @array[i] = obj 18 | end 19 | 20 | def min 21 | raise ContainerEmpty if @count == 0 22 | @array[1] 23 | end 24 | 25 | def dequeueMin 26 | raise ContainerEmpty if @count == 0 27 | result = @array[1] 28 | last = @array[@count] 29 | @count -= 1 30 | i = 1 31 | while 2 * i < @count + 1 32 | child = 2 * i 33 | if (child + 1 < @count + 1) && (@array[child + 1] < @array[child]) 34 | child += 1 35 | end 36 | break if last <= @array[child] 37 | @array[i] = @array[child] 38 | i = child 39 | end 40 | @array[i] = last 41 | result 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /algorithms/sort/selection.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Selection sort: A naive sort that goes through the container and selects the 3 | # smallest element, putting it at the beginning. Repeat until the end is reached. 4 | # Requirements: Needs to be able to compare elements with <=>, and the [] []= 5 | # methods should be implemented for the container. 6 | # Time Complexity: О(n^2) 7 | # Space Complexity: О(n) total, O(1) auxiliary 8 | # Stable: Yes 9 | # 10 | # [5, 4, 3, 1, 2].selection_sort! => [1, 2, 3, 4, 5] 11 | 12 | class Array 13 | def swap(first, second) 14 | self[first], self[second] = self[second], self[first] 15 | self 16 | end 17 | 18 | def selection_sort! 19 | 0.upto(length - 1) do |i| 20 | min = i 21 | (i + 1).upto(length - 1) do |j| 22 | min = j if (self[j] <=> self[min]) == -1 23 | end 24 | swap min, i 25 | end 26 | 27 | self 28 | end 29 | 30 | # def selection_sort! 31 | # for i in 0..length - 2 32 | # min = i 33 | # for j in (i + 1)...length 34 | # min = j if self[j] < self[min] 35 | # end 36 | # swap min, i 37 | # end 38 | # 39 | # self 40 | # end 41 | end 42 | -------------------------------------------------------------------------------- /structures/queue_as_array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class QueueAsArray < Queue 9 | def initialize(size = 0) 10 | super() 11 | @array = Array.new(size) 12 | @head = 0 13 | @tail = size - 1 14 | end 15 | 16 | def purge 17 | while @count > 0 18 | @array[@head] = nil 19 | @head += 1 20 | @head = 0 if @head == @array.length 21 | @count -= 1 22 | end 23 | end 24 | 25 | def head 26 | raise ContainerEmpty if @count == 0 27 | @array[@head] 28 | end 29 | 30 | def enqueue(obj) 31 | raise ContainerFull if @count == @array.length 32 | @tail += 1 33 | @tail = 0 if @tail == @array.length 34 | @array[@tail] = obj 35 | @count += 1 36 | end 37 | 38 | def dequeue 39 | raise ContainerEmpty if @count == 0 40 | result = @array[@head] 41 | @array[@head] = nil 42 | @head += 1 43 | @head = 0 if @head == @array.length 44 | @count -= 1 45 | result 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /algorithms/sort/heap.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Heap sort: Uses a heap (implemented by the Containers module) to sort the 3 | # collection. Requirements: Needs to be able to compare elements with <=> 4 | # Time Complexity: О(n^2) 5 | # Space Complexity: О(n) total, O(1) auxiliary 6 | # Stable: Yes 7 | # 8 | # [5, 4, 3, 1, 2].heap_sort! => [1, 2, 3, 4, 5] 9 | 10 | class Array 11 | def swap(first, second) 12 | self[first], self[second] = self[second], self[first] 13 | self 14 | end 15 | 16 | def heap_sort! 17 | self.dup.heap_sort 18 | end 19 | 20 | def heap_sort 21 | ((length - 2) / 2).downto(0) { |start| sift_down(start, length - 1) } 22 | 23 | (length - 1).downto(1) do |end_| 24 | swap 0, end_ 25 | sift_down(0, end_ - 1) 26 | end 27 | self 28 | end 29 | 30 | def sift_down(start, end_) 31 | root = start 32 | loop do 33 | child = root * 2 + 1 34 | break if child > end_ 35 | child += 1 if child + 1 <= end_ && self[child] < self[child + 1] 36 | if self[root] < self[child] 37 | swap child, root 38 | root = child 39 | else 40 | break 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /structures/deque_as_array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class DequeAsArray < QueueAsArray 9 | alias queueHead head 10 | 11 | include DequeMethods 12 | 13 | def initialize(size = 0) 14 | super(size) 15 | end 16 | 17 | alias head queueHead 18 | 19 | def enqueueHead(obj) 20 | raise ContainerFull if @count == @array.length 21 | @head = if @head == 0 22 | @array.length - 1 23 | else 24 | @head - 1 25 | end 26 | @array[@head] = obj 27 | @count += 1 28 | end 29 | 30 | alias dequeueHead dequeue 31 | 32 | def tail 33 | raise ContainerEmpty if @count == 0 34 | @array[@tail] 35 | end 36 | 37 | alias enqueueTail enqueue 38 | 39 | def dequeueTail 40 | raise ContainerEmpty if @count == 0 41 | result = @array[@tail] 42 | @array[@tail] = nil 43 | @tail = if @tail == 0 44 | @array.length - 1 45 | else 46 | @tail - 1 47 | end 48 | @count -= 1 49 | result 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /gotchas/method_missing_and_respond_to_missing.rb: -------------------------------------------------------------------------------- 1 | # When overriding #method_missing, remember to override #respond_to_missing? as 2 | # well. When you use method_missing to have an object return something on a 3 | # method call, always make sure you also redefine respond_to_missing?. If you 4 | # don't do it, nothing will break at a first glance, but you will run into trouble 5 | # eventually. Consider this class: 6 | 7 | class Dog 8 | def method_missing(method_name, *args, &block) 9 | if method_name == :bark 10 | 'woof!' 11 | else 12 | super 13 | end 14 | end 15 | 16 | end 17 | 18 | 19 | Dog.new.bark 20 | # => "woof!" 21 | 22 | Dog.new.respond_to? :bark 23 | # => false 24 | 25 | # Lots of code (gems or your own) relies on respond_to? (for a good reason). 26 | # You need to patch respond_to_missing? as well: 27 | 28 | class Dog 29 | def method_missing(method_name, *args, &block) 30 | if method_name == :bark 31 | 'woof!' 32 | else 33 | super 34 | end 35 | end 36 | 37 | def respond_to_missing?(method_name, *args) 38 | method_name == :bark or super 39 | end 40 | end 41 | 42 | Dog.new.bark 43 | # => "woof!" 44 | 45 | Dog.new.respond_to? :bark 46 | # => true 47 | -------------------------------------------------------------------------------- /test/algorithms_sort_test.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | 3 | require_relative '../algorithms/sort/bubble' 4 | require_relative '../algorithms/sort/heap' 5 | require_relative '../algorithms/sort/insertion' 6 | require_relative '../algorithms/sort/merge' 7 | require_relative '../algorithms/sort/quick' 8 | require_relative '../algorithms/sort/selection' 9 | require_relative '../algorithms/sort/shell' 10 | 11 | class AlgorithmsTest < Test::Unit::TestCase 12 | SORTED = [1, 2, 3, 4, 5].freeze 13 | UNSORTED = [5, 4, 3, 2, 1].freeze 14 | 15 | def test_bubdle_sort 16 | assert_equal UNSORTED.dup.bubble_sort!, SORTED 17 | end 18 | 19 | def test_heap_sort 20 | assert_equal UNSORTED.dup.heap_sort!, SORTED 21 | end 22 | 23 | def test_insertion_sort 24 | assert_equal UNSORTED.dup.insertion_sort!, SORTED 25 | end 26 | 27 | def test_merge_sort 28 | assert_equal UNSORTED.dup.merge_sort!, SORTED 29 | end 30 | 31 | def test_quick_sort 32 | assert_equal UNSORTED.dup.quick_sort!, SORTED 33 | end 34 | 35 | def test_selection_sort 36 | assert_equal UNSORTED.dup.selection_sort!, SORTED 37 | end 38 | 39 | def test_shell_sort 40 | assert_equal UNSORTED.dup.shell_sort!, SORTED 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /patterns/structural/composite.rb: -------------------------------------------------------------------------------- 1 | # In software engineering, the composite pattern is a partitioning design 2 | # pattern. The composite pattern describes that a group of objects is to be 3 | # treated in the same way as a single instance of an object. The intent of a 4 | # composite is to "compose" objects into tree structures to represent part-whole 5 | # hierarchies. Implementing the composite pattern lets clients treat individual 6 | # objects and compositions uniformly 7 | 8 | # Tasks witch are used in composition 9 | class MonsterTask 10 | attr_reader :reward 11 | 12 | def initialize 13 | @reward = 100 14 | end 15 | end 16 | 17 | class PuzzleTask 18 | attr_reader :reward 19 | 20 | def initialize 21 | @reward = 200 22 | end 23 | end 24 | 25 | # Composer 26 | class Quest 27 | def initialize 28 | # Here we use Array as example of data structure for composition. Always is used Tree 29 | @tasks = [] 30 | end 31 | 32 | def <<(task) 33 | @tasks << task 34 | end 35 | 36 | def reward 37 | @tasks.inject(0) { |sum, task| sum += task.reward } 38 | end 39 | end 40 | 41 | # Usage 42 | quest = Quest.new 43 | quest << MonsterTask.new 44 | quest << PuzzleTask.new 45 | puts quest.reward # => 300 46 | -------------------------------------------------------------------------------- /structures/array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class Array 9 | attr_accessor :baseIndex 10 | alias init initialize 11 | alias getitem [] 12 | alias setitem []= 13 | 14 | def initialize(size = 0, baseIndex = 0) 15 | init(size, nil) 16 | @baseIndex = baseIndex 17 | end 18 | 19 | def length=(value) 20 | tmp = Array.new(value, nil) 21 | 22 | for i in 0...[length, value].min 23 | tmp.setitem(i, getitem(i)) 24 | end 25 | 26 | clear 27 | setitem(value - 1, nil) if value > 0 28 | 29 | for i in 0...tmp.length 30 | setitem(i, tmp.getitem(i)) 31 | end 32 | end 33 | 34 | protected :getitem, :setitem 35 | 36 | def getOffset(index) 37 | @baseIndex = 0 if @baseIndex.nil? 38 | raise IndexError unless (@baseIndex...@baseIndex + length) === index 39 | index - @baseIndex 40 | end 41 | 42 | def [](index) 43 | getitem(getOffset(index)) 44 | end 45 | 46 | def []=(index, value) 47 | setitem(getOffset(index), value) 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /meta_programming/blank_slate.rb: -------------------------------------------------------------------------------- 1 | # Blank Slate 2 | # Prevents issues when using "Dynamic Proxies" 3 | # 4 | # e.g. user calls a method that exists higher up the inheritance chain 5 | # so your `method_missing` doesn't fire because the method does exist. 6 | # 7 | # To work around this issue, make sure your class starts with a "Blank Slate" 8 | # So you remove any methods you don't want to appear at all in the inheritance chain 9 | # by using `undef_method` (there is also `remove_method` which doesn't remove the named 10 | # method from the inheritance chain but just the current class, but that doesn't help us 11 | # fix the "Dynamic Proxy" scenario so we use `undef_method` instead). 12 | # 13 | # For "Dynamic Proxy" we use the parent `method_missing` so we keep that, 14 | # we also might use `respond_to?` so we keep that (although you can remove it if you don't). 15 | # Also the `__` in the below regex pattern is to prevent Ruby from displaying a warning 16 | # about removing 'reserved' methods such as `__id__` and `__send__` 17 | class ImBlank 18 | instance_methods.each do |m| 19 | undef_method m unless m.to_s.match?(/^__|method_missing|respond_to?/) 20 | end 21 | 22 | # rest of your code (such as your "Dynamic Proxy" implementation) 23 | end 24 | -------------------------------------------------------------------------------- /gotchas/private_data.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Private Class Methods in Ruby 3 | # Every object oriented language has a different take on the rules of methods and 4 | # objects and classes, and I cut my teeth on Java, which means that forever and 5 | # ever after I’ll assume everything works like Java. That left me writing code 6 | # like the `Apple` class below that has a private class method, but not really. 7 | # You can still call it. 8 | 9 | class Apple 10 | def self.latin_name 11 | binomial_name 12 | end 13 | 14 | private 15 | 16 | def self.binomial_name 17 | 'Malus domestica' 18 | end 19 | end 20 | 21 | Apple.latin_name 22 | Apple.binomial_name 23 | # no error, works fine 24 | 25 | # There’s a way to make class methods private in Ruby, you just gotta jump 26 | # through some hoops. Err, I mean use the `class << self` syntax. This oddity 27 | # pushes an instance singleton onto the class effectively creating class methods. 28 | 29 | class Orange 30 | def self.latin_name 31 | binomial_name 32 | end 33 | 34 | class << self 35 | private def binomial_name 36 | 'Citrus × sinensis' 37 | end 38 | end 39 | end 40 | 41 | Orange.latin_name 42 | Orange.binomial_name 43 | # ERROR!!! you can't call a private method. 44 | -------------------------------------------------------------------------------- /patterns/structural/decorator.rb: -------------------------------------------------------------------------------- 1 | # The decorator design pattern allows for features to be added dynamically to an 2 | # existing object. 3 | 4 | class Decorator 5 | def initialize(item) 6 | @item = item 7 | end 8 | 9 | def use 10 | item.use 11 | end 12 | 13 | # Dynamic method that added in decorator 14 | def another_use 15 | item.use + 'another way' 16 | end 17 | end 18 | 19 | class MagicDecorator < Decorator 20 | def description 21 | @item.description + 'Magic' 22 | end 23 | end 24 | 25 | class MasterpieceDecorator < Decorator 26 | def description 27 | @item.description + 'Masterpiece' 28 | end 29 | end 30 | 31 | # Class what should be decorated 32 | class Item 33 | attr_reader :description 34 | 35 | def initialize 36 | @description = 'Item ' 37 | end 38 | 39 | def use 40 | 'use it' 41 | end 42 | end 43 | 44 | # Usage 45 | item = Item.new 46 | puts item.description # => Item 47 | 48 | magic_item = MagicDecorator.new(item) 49 | puts magic_item.description # => Item Magic 50 | 51 | masterpiece_item = MasterpieceDecorator.new(item) 52 | puts masterpiece_item.description # => Item Masterpiece 53 | 54 | # All next lines puts "use it" 55 | item.use 56 | magic_item.use 57 | masterpiece_item.use 58 | -------------------------------------------------------------------------------- /structures/multi_dimensional_array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class MultiDimensionalArray 9 | def initialize(*dimensions) 10 | @dimensions = Array.new(dimensions.length) 11 | @factors = Array.new(dimensions.length) 12 | 13 | product = 1 14 | i = dimensions.length - 1 15 | while i >= 0 16 | @dimensions[i] = dimensions[i] 17 | @factors[i] = product 18 | product *= @dimensions[i] 19 | i -= 1 20 | end 21 | 22 | @data = Array.new(product) 23 | end 24 | 25 | def getOffset(indices) 26 | raise IndexError if indices.length != @dimensions.length 27 | 28 | offset = 0 29 | for i in 0...@dimensions.length 30 | raise IndexError if (indices[i] < 0) || (indices[i] >= @dimensions[i]) 31 | offset += @factors[i] * indices[i] 32 | end 33 | 34 | offset 35 | end 36 | 37 | def [](*indices) 38 | @data[getOffset(indices)] 39 | end 40 | 41 | def []=(*indicesAndValue) 42 | value = indicesAndValue.pop 43 | @data[getOffset(indicesAndValue)] = value 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /gotchas/braces_vs_do_end.rb: -------------------------------------------------------------------------------- 1 | # The general convention is to use do..end for multi-line blocks and curly braces 2 | # for single line blocks, but there is also a difference between the two that can 3 | # be illustrated with this example: 4 | 5 | puts [1, 2, 3].map { |k| k + 1 } 6 | # 2 7 | # 3 8 | # 4 9 | # => nil 10 | 11 | puts [1, 2, 3].map do |k| k + 1; end 12 | # 13 | # => nil 14 | 15 | # This means that {} has a higher precedence than do ..end, so keep that in mind 16 | # when deciding what you want to use. P.S: One more example to keep in mind while 17 | # you develop your preferences. The following code: 18 | 19 | task rake: pre_rake_task do 20 | something 21 | end 22 | 23 | # Really means: 24 | 25 | task(rake: pre_rake_task) { something } 26 | 27 | # And this code: 28 | 29 | task rake: pre_rake_task { 30 | something 31 | } 32 | 33 | # Really means: 34 | 35 | task rake: (pre_rake_task { something }) 36 | 37 | # So to get the actual definition that you want, with curly braces, you must do: 38 | 39 | task(rake: pre_rake_task) do 40 | something 41 | end 42 | 43 | # Maybe using braces for parameters is something you want to do anyways, but if 44 | # you don 't it' s probably best to use do .. end in these cases to avoid this 45 | # confusion. 46 | -------------------------------------------------------------------------------- /patterns/behavioral/memento.rb: -------------------------------------------------------------------------------- 1 | # Without violating encapsulation, capture and externalize an object's internal 2 | # state allowing the object to be restored to this state later. 3 | 4 | # The Originator can save and load itself. The Caretaker (the main function in 5 | # this case) never has to touch the memento objects. 6 | 7 | # This implementation is a bit naive: 8 | # - saves should be kept in files 9 | # - Marshal will not always work (singleton methods, bindings, etc..) 10 | 11 | module Originator 12 | def saves 13 | @saves ||= {} 14 | end 15 | 16 | def save(key) 17 | puts "saving key: #{key}" 18 | @saves[key] = Marshal.dump self 19 | end 20 | 21 | def restore(key) 22 | puts "restoring key: #{key}" 23 | include_state Marshal.load(@saves[key]) 24 | end 25 | 26 | private 27 | 28 | def include_state(other) 29 | other.instance_variables.each { |v| instance_variable_set(v, other.instance_variable_get(v)) unless v == '@saves' } 30 | end 31 | end 32 | 33 | class Example 34 | include Originator 35 | 36 | attr_accessor :name, :color 37 | 38 | def initialize(name, color) 39 | @name = name 40 | @color = color 41 | end 42 | end 43 | 44 | # Usage 45 | example = Example.new('Dave', 'blue') 46 | example.save :now # => saving key now 47 | -------------------------------------------------------------------------------- /meta_programming/class_macros.rb: -------------------------------------------------------------------------------- 1 | # Class Macros are just regular class methods that are only used in a class definition 2 | # e.g. not used from a new instance of the class (only at the time the class is defined) 3 | # 4 | # Below is an example of a Class Macro that alerts users of a publically available class 5 | # that the methods they've been using are now deprecated and they should use the renamed version. 6 | # 7 | # It uses "Dynamic Method" to help performance by creating the old methods again and delegating off 8 | # to the new methods (rather than using `method_missing` which can be quite slow as it has to spend 9 | # time looking up the inheritance chain) 10 | class Foo 11 | def get_a 12 | puts "I'm an A" 13 | end 14 | 15 | def get_b 16 | puts "I'm an B" 17 | end 18 | 19 | def get_c 20 | puts "I'm an C" 21 | end 22 | 23 | # Defining our Class Macro 24 | def self.deprecate(old_method, new_method) 25 | define_method(old_method) do |*args, &block| 26 | puts "Warning: #{old_method} is deprecated! Use #{new_method} instead" 27 | send(new_method, *args, &block) # `self` is assumed when calling `send` 28 | end 29 | end 30 | 31 | # Using our Class Macro 32 | deprecate :a, :get_a 33 | deprecate :b, :get_b 34 | deprecate :c, :get_c 35 | end 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /spec/examples.txt 9 | /test/tmp/ 10 | /test/version_tmp/ 11 | /tmp/ 12 | 13 | # Used by dotenv library to load environment variables. 14 | # .env 15 | 16 | ## Specific to RubyMotion: 17 | .dat* 18 | .repl_history 19 | build/ 20 | *.bridgesupport 21 | build-iPhoneOS/ 22 | build-iPhoneSimulator/ 23 | 24 | ## Specific to RubyMotion (use of CocoaPods): 25 | # 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 29 | # 30 | # vendor/Pods/ 31 | 32 | ## Documentation cache and generated files: 33 | /.yardoc/ 34 | /_yardoc/ 35 | /doc/ 36 | /rdoc/ 37 | 38 | ## Environment normalization: 39 | /.bundle/ 40 | /vendor/bundle 41 | /lib/bundler/man/ 42 | 43 | # for a library or gem, you might want to ignore these files since the code is 44 | # intended to run in multiple environments; otherwise, check them in: 45 | # Gemfile.lock 46 | # .ruby-version 47 | # .ruby-gemset 48 | 49 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 50 | .rvmrc 51 | 52 | # Jetbrains 53 | .idea/* 54 | -------------------------------------------------------------------------------- /algorithms/sort/bubble.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Bubble sort: A very naive sort that keeps swapping elements until the container 3 | # is sorted. Requirements: Needs to be able to compare elements with <=>, and 4 | # the [] []= methods should be implemented for the container. Time Complexity: 5 | # О(n^2). Space Complexity: О(n) total, O(1) auxiliary. Stable: Yes. 6 | # 7 | # [5, 4, 3, 1, 2].bubble_sort! => [1, 2, 3, 4, 5] 8 | 9 | class Array 10 | def swap(first, second) 11 | self[first], self[second] = self[second], self[first] 12 | self 13 | end 14 | 15 | def bubble_sort! 16 | loop do 17 | swapped = false 18 | (self.size - 1).times do |index| 19 | if self[index] > self[index + 1] 20 | swap index, index + 1 21 | swapped = true 22 | end 23 | end 24 | break unless swapped 25 | end 26 | 27 | self 28 | end 29 | 30 | # def bubble_sort! 31 | # length.times do |j| 32 | # for i in 1...(length - j) 33 | # swap i - 1, i if self[i - 1] < self[i] 34 | # end 35 | # end 36 | # 37 | # self 38 | # end 39 | # 40 | # def bubble_sort! 41 | # each_index do |index| 42 | # (length - 1).downto( index ) do |i| 43 | # swap i - 1, i if self[i - 1] < self[i] 44 | # end 45 | # end 46 | # 47 | # self 48 | # end 49 | end 50 | -------------------------------------------------------------------------------- /structures/double_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class DoubleLinkedList < SinglyLinkedList 9 | class Element < SinglyLinkedList < Element 10 | attr_accessor :prev 11 | 12 | def initialize(list, data, succ, prev) 13 | super(list, data, succ) 14 | @prev = prev 15 | end 16 | 17 | def insert_after(item) 18 | # Todo 19 | end 20 | 21 | def insert_before(item) 22 | # Todo 23 | end 24 | end 25 | 26 | attr_accessor :head, :tail 27 | 28 | def initialize 29 | @head = nil 30 | @tail = nil 31 | end 32 | 33 | def purge 34 | @head = nil 35 | @tail = nil 36 | end 37 | 38 | def empty? 39 | @head.nil? 40 | end 41 | 42 | def first 43 | if @head.nil? 44 | raise Error 45 | else 46 | @head.data 47 | end 48 | end 49 | 50 | def last 51 | if @tail.nil? 52 | raise Error 53 | else 54 | @tail.data 55 | end 56 | end 57 | 58 | def prepend(elem) 59 | # Todo 60 | end 61 | 62 | def append(elem) 63 | # Todo 64 | end 65 | 66 | def clone 67 | # Todo 68 | end 69 | 70 | def extract 71 | # Todo 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /structures/stack_as_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class StackAsLinkedList < Stack 9 | def initialize 10 | super 11 | @list = SinglyLinkedList.new 12 | end 13 | 14 | def purge 15 | @list.purge 16 | super 17 | end 18 | 19 | def push(obj) 20 | @list.prepend(obj) 21 | @count += 1 22 | end 23 | 24 | def pop 25 | raise ContainerEmpty if @count == 0 26 | result = @list.first 27 | @list.extract(result) 28 | @count -= 1 29 | result 30 | end 31 | 32 | def top 33 | raise ContainerEmpty if @count == 0 34 | @list.first 35 | end 36 | 37 | def each(&block) 38 | @list.each(&block) 39 | end 40 | 41 | attr_reader :list 42 | 43 | class Iterator < Opus8::Iterator 44 | def initialize(stack) 45 | @position = stack.list.head 46 | end 47 | 48 | def more? 49 | !@position.nil? 50 | end 51 | 52 | def succ 53 | if more? 54 | result = @position.datum 55 | @position = @position.succ 56 | else 57 | result = nil 58 | end 59 | result 60 | end 61 | end 62 | 63 | def iter 64 | Iterator.new(self) 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /structures/hash_table.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class HashTable < SearchableContainer 9 | def initialize 10 | super 11 | end 12 | 13 | abstractmethod :length 14 | 15 | def loadFactor 16 | count / length 17 | end 18 | 19 | def f(obj) 20 | obj.hash 21 | end 22 | 23 | def g(x) 24 | x.abs % length 25 | end 26 | 27 | def h(obj) 28 | g(f(obj)) 29 | end 30 | end 31 | 32 | class ChainedHashTable < HashTable 33 | def initialize(length) 34 | super() 35 | @array = Array.new(length) 36 | for i in 0...length 37 | @array[i] = SinglyLinkedList.new 38 | end 39 | end 40 | 41 | def length 42 | @array.length 43 | end 44 | 45 | def purge 46 | for i in 0...length 47 | @array[i].purge 48 | end 49 | @count = 0 50 | end 51 | 52 | def insert(obj) 53 | @array[h(obj)].append(obj) 54 | @count += 1 55 | end 56 | 57 | def withdraw(obj) 58 | @array[h(obj)].extract(obj) 59 | @count -= 1 60 | end 61 | 62 | def find(obj) 63 | ptr = @array[h(obj)].head 64 | until ptr.nil? 65 | return ptr.datum if ptr.datum == obj 66 | ptr = ptr.succ 67 | end 68 | nil 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /patterns/creational/prototype.rb: -------------------------------------------------------------------------------- 1 | # The prototype pattern is a creational pattern along the lines of the factory. 2 | # The trick with the prototype is that you create new objects by copying a master 3 | # object. Change that master object and all subsequent objects that you create 4 | # will go off into life with a copy of the change. 5 | 6 | # Prototypes witch are used for prototyping 7 | class Note 8 | attr_accessor :duration 9 | 10 | def initialize(duration) 11 | @duration = duration 12 | end 13 | 14 | def deep_copy 15 | Marshal.load(Marshal.dump(self)) 16 | end 17 | end 18 | 19 | class Clef 20 | # Implementation 21 | end 22 | 23 | class PrototypeManager 24 | def initialize 25 | @prototypes = {} 26 | end 27 | 28 | def set(key, prototype) 29 | if @prototypes.include? key 30 | raise IndexError, "A prototype is already assigned to this key: #{key}" 31 | else 32 | @prototypes[key] = prototype 33 | end 34 | end 35 | 36 | def unset(key) 37 | if !@prototypes.include? key 38 | raise IndexError, "This key doesn't exist in prototype: #{key}" 39 | else 40 | @prototypes.delete key 41 | end 42 | end 43 | 44 | def get(key) 45 | @prototypes[key].deep_copy 46 | end 47 | end 48 | 49 | # Usage 50 | 51 | prototype = PrototypeManager.new 52 | prototype.set :half_note, Note.new(0.5) 53 | prototype.set :full_note, Note.new(1) 54 | 55 | note = prototype.get(:full_note) 56 | note.duration 57 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 2.4 3 | Include: 4 | - '../**/Rakefile' 5 | - '../**/config.ru' 6 | Exclude: 7 | - '../bin/**/*' 8 | - '../db/schema.rb' 9 | - '../db/migrate/*' 10 | - '../config/**/*' 11 | - '../lib/tasks/*' 12 | 13 | Documentation: 14 | Enabled: false 15 | 16 | Layout/ClassStructure: 17 | Categories: 18 | module_inclusion: 19 | - extend 20 | - include 21 | - prepend 22 | association: 23 | - has_one 24 | - has_many 25 | attribute: 26 | - attr_accessor 27 | - attr_reader 28 | - attr_writer 29 | ExpectedOrder: 30 | - module_inclusion 31 | - constants 32 | - public_class_methods 33 | - initializer 34 | - public_methods 35 | - protected_methods 36 | - private_methods 37 | 38 | Lint/EndAlignment: 39 | EnforcedStyleAlignWith: variable 40 | 41 | Layout/IndentArray: 42 | EnforcedStyle: consistent 43 | 44 | Layout/IndentHash: 45 | EnforcedStyle: consistent 46 | 47 | Layout/MultilineAssignmentLayout: 48 | EnforcedStyle: same_line 49 | 50 | Metrics/BlockLength: 51 | Exclude: 52 | - '../spec/**/*' 53 | 54 | Metrics/LineLength: 55 | Max: 120 56 | 57 | Naming/FileName: 58 | Exclude: 59 | - '../**/Gemfile' 60 | - '../**/Rakefile' 61 | 62 | Style/PercentLiteralDelimiters: 63 | PreferredDelimiters: 64 | default: [] 65 | '%i': '[]' 66 | '%I': '[]' 67 | '%w': '[]' 68 | '%W': '[]' 69 | '%r': '{}' -------------------------------------------------------------------------------- /algorithms/search/khuth-morris-pratt.rb: -------------------------------------------------------------------------------- 1 | # Knuth-Morris-Pratt Algorithm substring search algorithm: Efficiently finds the 2 | # starting position of a substring in a string. The algorithm calculates the best 3 | # position to resume searching from if a failure occurs. 4 | # 5 | # The method returns the index of the starting position in the string where the 6 | # substring is found. If there is no match, nil is returned. 7 | # 8 | # Complexity: O(n + k), where n is the length of the string and k is the length 9 | # of the substring. 10 | # 11 | # "ABC ABCDAB ABCDABCDABDE".kmp_search("ABCDABD") #=> 15 12 | # "ABC ABCDAB ABCDABCDABDE".kmp_search("ABCDEF") #=> nil 13 | 14 | class String 15 | def kmp_search(substring) 16 | return nil if self.nil? || substring.nil? 17 | 18 | pos = 2 19 | cnd = 0 20 | failure_table = [-1, 0] 21 | while pos < substring.length 22 | if substring[pos - 1] == substring[cnd] 23 | failure_table[pos] = cnd + 1 24 | pos += 1 25 | cnd += 1 26 | elsif cnd.positive? 27 | cnd = failure_table[cnd] 28 | else 29 | failure_table[pos] = 0 30 | pos += 1 31 | end 32 | end 33 | 34 | m = i = 0 35 | while m + i < self.length 36 | if substring[i] == self[m + i] 37 | i += 1 38 | return m if i == substring.length 39 | else 40 | m = m + i - failure_table[i] 41 | i = failure_table[i] if i.positive? 42 | end 43 | end 44 | 45 | nil 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /patterns/behavioral/observer.rb: -------------------------------------------------------------------------------- 1 | # An object, called the subject, maintains a list of its dependents, called 2 | # observers, and notifies them automatically of any state changes, usually by 3 | # calling one of their methods. 4 | 5 | module Observable 6 | def initialize 7 | @observers = [] 8 | end 9 | 10 | def add_observer(observer) 11 | @observers << observer unless @observers.include?(observer) 12 | end 13 | 14 | def delete_observer(observer) 15 | @observers.delete(observer) 16 | end 17 | 18 | def notify_observers 19 | @observers.each { |x| x.update(self) } 20 | end 21 | end 22 | 23 | class Employee 24 | include Observable 25 | 26 | attr_reader :name 27 | attr_accessor :title, :salary 28 | 29 | def initialize(name, title, salary) 30 | super() 31 | @name = name 32 | @title = title 33 | @salary = salary 34 | end 35 | end 36 | 37 | class BaseObserver 38 | def update 39 | raise 'Must be implement "update" function' 40 | end 41 | end 42 | 43 | class Payroll < BaseObserver 44 | def update(employee) 45 | p("Cut a new check for #{employee.name}!") 46 | p("His salary is now #{employee.salary}!") 47 | end 48 | end 49 | 50 | class TaxMan < BaseObserver 51 | def update(employee) 52 | p("Send #{employee.name} a new tax bill!") 53 | end 54 | end 55 | 56 | # Usage 57 | mike = Employee.new('Mike', 'project manger', 25_000) 58 | 59 | mike.add_observer(Payroll.new) 60 | mike.add_observer(TaxMan.new) 61 | 62 | mike.salary = 35_000 63 | mike.title = 'senior project manger' 64 | mike.notify_observers 65 | -------------------------------------------------------------------------------- /structures/binary_tree.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class BinaryTree < Tree 9 | def initialize(*args) 10 | super() 11 | case args.length 12 | when 0 13 | @key = nil 14 | @left = nil 15 | @right = nil 16 | when 1 17 | @key = args[0] 18 | @left = BinaryTree.new 19 | @right = BinaryTree.new 20 | when 3 21 | @key = args[0] 22 | @left = args[1] 23 | @right = args[2] 24 | else 25 | raise ArgumentError 26 | end 27 | end 28 | 29 | def purge 30 | @key = nil 31 | @left = nil 32 | @right = nil 33 | end 34 | 35 | def left 36 | raise StateError if empty? 37 | @left 38 | end 39 | 40 | def right 41 | raise StateError if empty? 42 | @right 43 | end 44 | 45 | def left 46 | raise StateError if empty? 47 | @left 48 | end 49 | 50 | def right 51 | raise StateError if empty? 52 | @right 53 | end 54 | 55 | def compareTo(bt) 56 | assert { is_a?(obj.type) } 57 | if empty? 58 | if bt.empty? 59 | return 0 60 | else 61 | return -1 62 | end 63 | elsif bt.empty? 64 | return 1 65 | else 66 | result = @key <=> bt._key 67 | result = @left <=> bt._left if result == 0 68 | result = @right <=> bt._right if result == 0 69 | return result 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /structures/stack_as_array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class StackAsArray < Stack 9 | def initialize(size = 0) 10 | super() 11 | @array = Array.new(size) 12 | end 13 | 14 | def purge 15 | while @count > 0 16 | @count -= 1 17 | @array[@count] = nil 18 | end 19 | end 20 | 21 | def push(obj) 22 | raise ContainerFull if @count == @array.length 23 | @array[@count] = obj 24 | @count += 1 25 | end 26 | 27 | def pop 28 | raise ContainerEmpty if @count == 0 29 | @count -= 1 30 | result = @array[@count] 31 | @array[@count] = nil 32 | result 33 | end 34 | 35 | def top 36 | raise ContainerEmpty if @count == 0 37 | @array[@count - 1] 38 | end 39 | 40 | def each 41 | for i in 0...@count 42 | yield @array[i] 43 | end 44 | end 45 | 46 | attr_reader :array 47 | 48 | class Iterator < Opus8::Iterator 49 | def initialize(stack) 50 | @stack = stack 51 | @position = 0 52 | end 53 | 54 | def more? 55 | @position < @stack.count 56 | end 57 | 58 | def succ 59 | if more? 60 | assert { more? } 61 | result = @stack.array[@position] 62 | @position += 1 63 | else 64 | result = nil 65 | end 66 | result 67 | end 68 | end 69 | 70 | def iter 71 | Iterator.new(self) 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /meta_programming/flattening_the_scope.rb: -------------------------------------------------------------------------------- 1 | # Flattening the Scope (aka Nested Lexical Scopes) 2 | # Where you change the code in such a way that it's easier for you to pass variables through "Scope Gates". 3 | # A scope gate is any block, where normally when you enter its scope the variables outside of it become unreachable. 4 | # This happens in: Class definitions, Module definitions, Method definitions 5 | # I'm not sure what the real life examples are of this, but if you ever wonder why some code does the following, 6 | # then maybe it was that they wanted to flatten the scope so they could more easily pass around variables. 7 | # I guess it's better to do it this way than to define a global variable? 8 | # 9 | # In the following code we want to access `my_var` from inside the method (inner scope gate) that's 10 | # inside the class (outer scope gate). 11 | my_var = 'abc' 12 | class OuterScopeGate 13 | puts my_var 14 | 15 | def inner_scope_gate 16 | puts my_var 17 | end 18 | end 19 | 20 | # We fix this by flattening the code into method calls (method *calls* aren't scope gates) 21 | # So we turn the class keyword into a method call using `Class.new` 22 | # We also turn the method inside the class from a keyword into a method call using `define_method` 23 | my_var = 'abc' 24 | MyClass = Class.new do 25 | puts "Here is 'my_var' inside my class definition: #{my_var}" 26 | 27 | define_method :my_method do 28 | puts "Here is 'my_var' inside my class instance method: #{my_var}" 29 | end 30 | end # => Here is 'my_var' inside my class definition: abc 31 | MyClass.new.my_method # => Here is 'my_var' inside my class instance method: abc 32 | -------------------------------------------------------------------------------- /patterns/behavioral/iterator.rb: -------------------------------------------------------------------------------- 1 | # Provide a way to access the elements of an aggregate object sequentially 2 | # without exposing its underlying representation. 3 | 4 | # Iteration entity 5 | class Account 6 | attr_reader :balance 7 | 8 | def initialize(balance) 9 | @balance = balance 10 | end 11 | end 12 | 13 | # Collection class 14 | class Bank 15 | include Enumerable 16 | 17 | def initialize 18 | @accounts = [] 19 | end 20 | 21 | def each(&block) 22 | @accounts.each(&block) 23 | end 24 | 25 | def add(account) 26 | @accounts << account 27 | end 28 | end 29 | 30 | # Usage 31 | bank = Bank.new 32 | bank.add Account.new(100) 33 | bank.add Account.new(150) 34 | bank.add Account.new(175) 35 | 36 | puts bank.map(&:balance) 37 | 38 | # Internal iterator 39 | class Collection 40 | def initialize(array) 41 | @array = array 42 | @index = 0 43 | end 44 | 45 | def next? 46 | @index < @array.length 47 | end 48 | 49 | def next_item 50 | value = @array[@index] 51 | @index += 1 52 | value 53 | end 54 | 55 | def iterate 56 | yield(next_item) if block_given? while next? 57 | end 58 | end 59 | 60 | # Usage 61 | collection = Collection.new([1, 2, 3, 4, 5]) 62 | collection.iterate { |elem| puts elem } 63 | 64 | # External iterator 65 | class Collection 66 | def initialize(array) 67 | @array = array 68 | @index = 0 69 | end 70 | 71 | def next? 72 | @index < @array.length 73 | end 74 | 75 | def next_item 76 | value = @array[@index] 77 | @index += 1 78 | value 79 | end 80 | end 81 | 82 | # Usage 83 | collection = Collection.new([1, 2, 3, 4, 5]) 84 | puts "Item: #{collection.next_item}" while collection.next? 85 | -------------------------------------------------------------------------------- /solid/dependency_inversion.rb: -------------------------------------------------------------------------------- 1 | # The Dependency Inversion Principle has to do with high-level (think business 2 | # logic) objects not depending on low-level (think database querying and IO) 3 | # implementation details. This can be achieved with duck typing and the 4 | # Dependency Inversion Principle. Often this pattern is used to achieve the 5 | # Open/Closed Principle that we discussed above. In fact, we can even reuse 6 | # that same example as a demonstration of this principle. 7 | 8 | # Now there is a formatter class, but I've hardcoded it on the Report class, 9 | # thus creating a dependency from the Report to the JSONFormatter. Since the 10 | # Report is a more abstract (high-level) concept than the JSONFormatter, we're 11 | # effectively breaking the DIP. 12 | 13 | class Report 14 | def body 15 | # Implementation 16 | end 17 | 18 | def print 19 | JSONFormatter.new.format(body) 20 | end 21 | end 22 | 23 | class JSONFormatter 24 | def format(body) 25 | # Implementation 26 | end 27 | end 28 | 29 | # Solution 30 | 31 | # This way the Report does not depend on the JSONFormatter and can use any type 32 | # of formatter that has a method called format (this is known as duck typing). 33 | # Another thing of note is that we've used, once again, dependency injection to 34 | # solve a problem. This technique is a very powerful one when our goal is 35 | # decoupling objects, and even though it has the same initials as the dependency 36 | # inversion principle (vs dependency injection pattern), they are completely 37 | # different concepts. 38 | 39 | class Report 40 | def body 41 | # Implementation 42 | end 43 | 44 | def print(formatter: JSONFormatter.new) 45 | formatter.format body 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /patterns/creational/builder.rb: -------------------------------------------------------------------------------- 1 | # The very simple idea behind the Builder pattern is that you encapsulate object 2 | # construction logic behind a class of its own. The builder class takes charge of 3 | # assembling all the components of a complex object. Each builder has an interface 4 | # that lets you specify the configuration of your new object step by step. In a 5 | # sense, a builder is a sort of like a multipart new method, where objects are 6 | # created in an extended process instead of all in one shot. 7 | 8 | # Classes witch are used in builder 9 | class Computer 10 | # Implementation 11 | end 12 | 13 | class TurboCPU 14 | # Implementation 15 | end 16 | 17 | class Drive 18 | # Implementation 19 | end 20 | 21 | # Builder 22 | class ComputerBuilder 23 | attr_reader :computer 24 | 25 | def initialize 26 | @computer = Computer.new 27 | end 28 | 29 | def turbo(_has_turbo_cpu = true) 30 | computer.cpu = TurboCPU.new 31 | end 32 | 33 | def display=(display) 34 | computer.display = display 35 | end 36 | 37 | def memory_size=(size_in_mb) 38 | computer.memory_size = size_in_mb 39 | end 40 | 41 | def add_cd(writer = false) 42 | computer.drives << Drive.new(:cd, 760, writer) 43 | end 44 | 45 | def add_dvd(writer = false) 46 | computer.drives << Drive.new(:dvd, 4000, writer) 47 | end 48 | 49 | def add_hard_disk(size_in_mb) 50 | computer.drives << Drive.new(:hard_disk, size_in_mb, true) 51 | end 52 | end 53 | 54 | # Usage 55 | 56 | computer_builder = ComputerBuilder.new 57 | 58 | computer_builder.turbo false 59 | computer_builder.display = 'Monitor' 60 | computer_builder.add_dvd false 61 | computer_builder.add_cd = false 62 | computer_builder.memory_size = '12mb' 63 | 64 | computer = computer_builder.computer 65 | -------------------------------------------------------------------------------- /gotchas/foo_bar.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | FOO = 123 3 | 4 | module Foo 5 | FOO = 555 6 | end 7 | 8 | module Foo 9 | class Bar 10 | def baz 11 | puts FOO 12 | end 13 | end 14 | end 15 | 16 | class Foo::Bar 17 | def glorf 18 | puts FOO 19 | end 20 | end 21 | 22 | Foo::Bar.new.baz 23 | # => 555 24 | 25 | Foo::Bar.new.glorf 26 | # => 123 27 | 28 | # You can think of each appearance of `module Something`, `class Something` or 29 | # `def something` as a “gateway” into a new scope. When Ruby is searching for the 30 | # definition of a name that has been referenced it first looks in the current 31 | # scope (the method, class or module), and if it isn’t found there it will go 32 | # back through each containing “gateway” and search the scope there. 33 | # In your example the method `baz` is defined as 34 | 35 | module Foo 36 | class Bar 37 | def baz 38 | puts FOO 39 | end 40 | end 41 | end 42 | 43 | # So when trying to determine the value of `FOO`, first the class `Bar` is 44 | # checked, and since `Bar` doesn’t contain a `FOO` the search moves up through 45 | # the class Bar "gateway” into the `Foo` module which is the containing scope. 46 | # `Foo` does contain a constant `FOO` (555) so this is the result you see. 47 | # The method `glorf` is defined as: 48 | 49 | class Foo::Bar 50 | def glorf 51 | puts FOO 52 | end 53 | end 54 | 55 | # Here the “gateway” is class Foo::Bar, so when `FOO` isn’t found inside `Bar` 56 | # the “gateway” passes through the `Foo` module and straight into the top level, 57 | # where there is another `FOO` (123) which is what is displayed. Note how using 58 | # `class Foo::Bar` creates a single “gateway”, skipping over the scope of `Foo`, 59 | # but `module Foo; class Bar` ... opens two separate “gateways” 60 | -------------------------------------------------------------------------------- /patterns/behavioral/visitor.rb: -------------------------------------------------------------------------------- 1 | # Represent an operation to be performed on the elements of an object structure. 2 | # Visitor lets a new operation be defined without changing the classes of the 3 | # elements on which it operates. 4 | 5 | module Node 6 | def accept(visitor) 7 | if is_a? StringNode 8 | visitor.visit_string self if visitor.respond_to? :visit_string 9 | elsif is_a? IntegerNode 10 | visitor.visit_int self if visitor.respond_to? :visit_int 11 | end 12 | end 13 | end 14 | 15 | class StringNode 16 | include Node 17 | attr_accessor :string 18 | 19 | def initialize(val) 20 | @string = val 21 | end 22 | end 23 | 24 | class IntegerNode 25 | include Node 26 | attr_accessor :int 27 | 28 | def initialize(val) 29 | @int = val 30 | end 31 | end 32 | 33 | class PrintingVisitor 34 | def visit_string(node) 35 | puts node.string 36 | end 37 | 38 | def visit_int(node) 39 | puts node.int 40 | end 41 | end 42 | 43 | class RevertingVisitor 44 | def visit_string(node) 45 | puts node.string.reverse! 46 | end 47 | end 48 | 49 | # Usage 50 | myTreeRoot = Tree::TreeNode.new('ROOT', StringNode.new('this is the root node')) 51 | 52 | myTreeRoot << Tree::TreeNode.new('child1', StringNode.new('madam im adam')) << Tree::TreeNode.new('grandchild1', IntegerNode.new(3)) << Tree::TreeNode.new('grandchild2', IntegerNode.new(2)) 53 | myTreeRoot << Tree::TreeNode.new('child2', StringNode.new('race car')) << Tree::TreeNode.new('grandchild3', StringNode.new('damn, i agassi miss again. mad')) 54 | 55 | puts 'PRINTING visitor...' 56 | @pvisitor = PrintingVisitor.new 57 | myTreeRoot.each { |node| node.content.accept @pvisitor } 58 | puts "\nREVERTING visitor..." 59 | 60 | @rvisitor = RevertingVisitor.new 61 | myTreeRoot.each { |node| node.content.accept @rvisitor } 62 | -------------------------------------------------------------------------------- /structures/b_tree.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class BTree < MWayTree 9 | def initialize(m) 10 | super 11 | @parent = nil 12 | end 13 | 14 | def attachSubtree(i, t) 15 | @subtree[i] = t 16 | t.parent = self 17 | end 18 | 19 | attr_accessor :parent 20 | 21 | def insert(obj) 22 | if empty? 23 | if @parent.nil? 24 | attachSubtree(0, BTree.new(m)) 25 | @key[1] = obj 26 | attachSubtree(1, BTree.new(m)) 27 | @count = 1 28 | else 29 | @parent.insertUp(obj, BTree.new(m)) 30 | end 31 | else 32 | index = findIndex(obj) 33 | raise ArgumentError if (index != 0) && (@key == obj) 34 | @subtree[index].insert(obj) 35 | end 36 | end 37 | 38 | def insertUp(obj, child) 39 | index = findIndex(obj) 40 | if !full? 41 | insertPair(index + 1, obj, child) 42 | @count += 1 43 | else 44 | extraKey, extraTree = insertPair(index + 1, obj, child) 45 | if @parent.nil? 46 | left = BTree.new(m) 47 | right = BTree.new(m) 48 | left.attachLeftHalfOf(self) 49 | right.attachRightHalfOf(self) 50 | right.insertUp(extraKey, extraTree) 51 | attachSubtree(0, left) 52 | @key[1] = @key[(m + 1) / 2] 53 | attachSubtree(1, right) 54 | @count = 1 55 | else 56 | @count = (m + 1) / 2 - 1 57 | right = BTree.new(m) 58 | right.attachRightHalfOf(self) 59 | right.insertUp(extraKey, extraTree) 60 | @parent.insertUp(@key[(m + 1) / 2], right) 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /patterns/behavioral/command.rb: -------------------------------------------------------------------------------- 1 | # The command pattern is a behavior design pattern used to store the information 2 | # necessary to call methods at a future time. 3 | 4 | # The command is merely a set of actions wrapped in an object. With ruby, we can 5 | # use Procs to do the same thing without the need to create a separate object. 6 | # This is a good option when the action is simple and doesn't require saving 7 | # state information, otherwise, a command class is the better option. 8 | 9 | # Invoker 10 | class Switch 11 | attr_reader :history 12 | 13 | def execute(cmd) 14 | @history ||= [] 15 | @history << cmd.execute 16 | end 17 | end 18 | 19 | # Command Interface 20 | class Command 21 | attr_reader :obj 22 | 23 | def initialize(obj) 24 | @obj = obj 25 | end 26 | 27 | def execute 28 | raise NotImplementedError 29 | end 30 | end 31 | 32 | # Command for turning on 33 | class TurnOnCommand < Command 34 | def execute 35 | obj.turn_on 36 | end 37 | end 38 | 39 | # Command for turning off 40 | class TurnOffCommand < Command 41 | def execute 42 | obj.turn_off 43 | end 44 | end 45 | 46 | # Receiver 47 | class Light 48 | def turn_on 49 | 'the light is on' 50 | end 51 | 52 | def turn_off 53 | 'the light is off' 54 | end 55 | end 56 | 57 | # Client 58 | class LightSwitchClient 59 | attr_reader :switch 60 | 61 | def initialize 62 | @lamp = Light.new 63 | @switch = Switch.new 64 | end 65 | 66 | def switch_for(cmd) 67 | case cmd 68 | when 'on' then @switch.execute(TurnOnCommand.new(@lamp)) 69 | when 'off' then @switch.execute(TurnOffCommand.new(@lamp)) 70 | else puts 'Sorry, I so sorry' 71 | end 72 | end 73 | end 74 | 75 | client = LightSwitchClient.new 76 | client.switch_for('on') 77 | client.switch_for('off') 78 | client.switch.history #=> ['the light is on', 'the light is off'] 79 | -------------------------------------------------------------------------------- /functional_programming/partial_application_and_currying.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Let’s first understand what these two different applications of functions are. 3 | # 4 | # Partial function application, and currying, are defined as such: 5 | # 6 | # Partial function aplication is calling a function with some number of arguments, 7 | # in order to get a function back that will take that many less arguments. 8 | # Currying is taking a function that takes n arguments, and splitting it 9 | # into n functions that take one argument. 10 | # 11 | # In order to give you a clearer idea of what each of these two things will 12 | # do a function, let’s take an example Proc: 13 | 14 | proc { |x, y, z| x + y + z } 15 | 16 | # Partial application of this function would return, if we passed in the first 17 | # two arguments, the following nested Procs: 18 | 19 | proc { |x, y| proc { |z| x + y + z } } 20 | 21 | # On the other hand, currying this function would return the following nested 22 | # Procs: 23 | 24 | proc { |x| proc { |y| proc { |z| x + y + z } } } 25 | 26 | # .curry returns a curried proc. If the optional arity argument is given, 27 | # it determines the number of arguments. A curried proc receives some arguments. 28 | # If a sufficient number of arguments are supplied, it passes the supplied 29 | # arguments to the original proc and returns the result. Otherwise, returns 30 | # another curried proc that takes the rest of arguments. 31 | 32 | l = ->(x, y, z) { x + y + z } 33 | l.curry[1][2][3] 34 | # => 6 35 | 36 | a = l.curry[1] 37 | # => 38 | 39 | b = a[2] 40 | #=> 41 | 42 | b[3] 43 | # => 6 44 | 45 | apply_math = ->(fn, a, b) { a.send fn, b } 46 | add = apply_math.curry.call(:+) 47 | add.call(1, 2) 48 | # => 3 49 | 50 | increment = add.curry.call(1) 51 | increment.call(1) 52 | # => 2 53 | 54 | increment.call(5) 55 | # => 6 56 | -------------------------------------------------------------------------------- /meta_programming/dynamic_method.rb: -------------------------------------------------------------------------------- 1 | # Dynamic Method 2 | # Allows us to dynamically create methods 3 | # define_method :method_name { block that becomes method body } 4 | class Foo 5 | define_method :bar do 6 | puts 'This is a dynamic method' 7 | end 8 | end 9 | Foo.new.bar # => "This is a dynamic method" 10 | 11 | # Dynamic Method 12 | # Alternative example 13 | class Bar 14 | # we have to define this method on `self` (see below comment) 15 | def self.create_method(method) 16 | define_method "my_#{method}" do 17 | puts "Dynamic method called 'my_#{method}'" 18 | end 19 | end 20 | 21 | # these methods are executed within the definition of the Bar class 22 | create_method :foo 23 | create_method :bar 24 | create_method :baz 25 | end 26 | Bar.new.my_foo # => "Dynamic method called 'my_foo'" 27 | Bar.new.my_bar # => "Dynamic method called 'my_bar'" 28 | Bar.new.my_baz # => "Dynamic method called 'my_baz'" 29 | 30 | # Dynamic Method 31 | # Parse another class for data 32 | class AnotherClass 33 | def get_foo_stuff; end 34 | 35 | def get_bar_stuff; end 36 | 37 | def get_baz_stuff; end 38 | end 39 | class Baz 40 | def initialize(a_class) 41 | a_class.methods.grep(/^get_(.*)_stuff$/) { Baz.create_method Regexp.last_match(1) } 42 | end 43 | 44 | def self.create_method(method) 45 | define_method "my_#{method}" do 46 | puts "Dynamic method called 'my_#{method}'" 47 | end 48 | end 49 | end 50 | another_class = AnotherClass.new 51 | Baz.new(another_class).my_foo # => "Dynamic method called 'my_foo'" 52 | Baz.new(another_class).my_bar # => "Dynamic method called 'my_bar'" 53 | Baz.new(another_class).my_baz # => "Dynamic method called 'my_baz'" 54 | 55 | class Foo 56 | def initialize(bar) 57 | self.class.send(:define_method, bar) { p "hello #{bar}!" } 58 | end 59 | end 60 | foo = Foo.new('world') 61 | foo.world # => "hello world!" 62 | -------------------------------------------------------------------------------- /patterns/creational/abstract_factory.rb: -------------------------------------------------------------------------------- 1 | # Abstract Factory patterns work around a super-factory which creates other 2 | # factories. This factory is also called as factory of factories. This type of 3 | # design pattern comes under creational pattern as this pattern provides one of 4 | # the best ways to create an object. 5 | 6 | # In Abstract Factory pattern an interface is responsible for creating a factory 7 | # of related objects without explicitly specifying their classes. Each generated 8 | # factory can give the objects as per the Factory pattern. 9 | 10 | # Classes witch is used in abstract factories 11 | 12 | class Frog 13 | # Implementation 14 | end 15 | 16 | class Algae 17 | # Implementation 18 | end 19 | 20 | class Tiger 21 | # Implementation 22 | end 23 | 24 | class Tree 25 | # Implementation 26 | end 27 | 28 | # Abstract factory with realization pond environment 29 | class PondFactory 30 | def new_animal 31 | Frog.new 32 | end 33 | 34 | def new_plant 35 | Algae.new 36 | end 37 | end 38 | 39 | # Abstract factory with realization jungle environment 40 | class JungleFactory 41 | def new_animal 42 | Tiger.new 43 | end 44 | 45 | def new_plant 46 | Tree.new 47 | end 48 | end 49 | 50 | # Factory of factories 51 | class EnvironmentFactory 52 | attr_reader :factories 53 | 54 | def initialize 55 | @factories = %w[PondFactory JungleFactory] 56 | end 57 | 58 | def get_factory(method) 59 | factory_class = get_factory_class method 60 | 61 | if factories.include? factory_class 62 | self.class.const_get(factory_class).new 63 | else 64 | super 65 | end 66 | end 67 | 68 | private 69 | 70 | def get_factory_class(method) 71 | "#{method.to_s.capitalize}Factory" 72 | end 73 | end 74 | 75 | # Usage 76 | environment = EnvironmentFactory.new 77 | jungle = environment.get_factory(:jungle) 78 | jungle.new_animal 79 | -------------------------------------------------------------------------------- /solid/liskov_substitution.rb: -------------------------------------------------------------------------------- 1 | # Liskov’s principle tends to be the most difficult to understand. The principle 2 | # states that you should be able to replace any instances of a parent class 3 | # with an instance of one of its children without creating any unexpected or 4 | # incorrect behaviors. 5 | 6 | class Rectangle 7 | def initialize(height, width) 8 | @height = height 9 | @width = width 10 | end 11 | 12 | def set_height(height) 13 | @height = height 14 | end 15 | 16 | def set_width(width) 17 | @width = width 18 | end 19 | 20 | def square 21 | @width * @height 22 | end 23 | end 24 | 25 | # Solution 26 | 27 | # LSP says is if we know the interface of Rectangle, We need to be able to guess 28 | # the interface of subtype class Square 29 | # Square.new(3).square => 9 30 | 31 | class Square < Rectangle 32 | def initialize(side) 33 | super(side, side) 34 | end 35 | 36 | def set_height(height) 37 | super(height) 38 | @width = height 39 | end 40 | 41 | def set_width(width) 42 | super(width) 43 | @height = width 44 | end 45 | end 46 | 47 | # Another common instance of a Liskov violation is raising an exception for an 48 | # overridden method in a child class. It’s also not uncommon to see methods 49 | # overridden with modified method signatures causing branching on type in 50 | # classes that depend on objects of the parent’s type. All of these either 51 | # lead to unstable code or unnecessary and ugly branching. 52 | 53 | class Animal 54 | def walk 55 | 'walking_as_animal' 56 | end 57 | end 58 | 59 | class Cat < Animal 60 | def run 61 | 'run_as_cat' 62 | end 63 | end 64 | 65 | # Solution 66 | 67 | class Animal 68 | def walk 69 | 'walking_as_animal' 70 | end 71 | 72 | def run 73 | raise NotImplementedError 74 | end 75 | end 76 | 77 | class Cat < Animal 78 | def run 79 | 'run_as_cat' 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /threads/mutex.rb: -------------------------------------------------------------------------------- 1 | # Mutexes provide a mechanism for multiply threads to synchronize access to a 2 | # critical portion of code. It helps bring some order and some guaranty to the 3 | # world of multi-threaded chaos. 4 | # In this program, since any thread has to lock the mutex before it can push to 5 | # the Array, there's a guarantee that no two threads will be performing this 6 | # operation at the same time. In other words, this operation can no longer be 7 | # interrupted before it's completed. Once one thread begins pushing to the 8 | # Array, no other threads will be able to enter that portion of code until the 9 | # first thread is finished. This operation is now thread-safe. 10 | 11 | shared_array = [] 12 | # Notice that the mutex is shared among all the threads. The guarantee only 13 | # works if the threads are sharing the same Mutex instance. In this way, when 14 | # one thread locks a mutex, others have to wait for it to be unlocked. 15 | mutex = Mutex.new 16 | 17 | 10.times.map do 18 | Thread.new do 19 | 1000.times do 20 | # Thread lock mutex and become owner, could be one one owner 21 | # Now only one thread can run wrapper code and update 22 | mutex.lock 23 | shared_array << nil 24 | mutex.unlock 25 | # After unlock mutex other threads can lock mutex 26 | 27 | # Other convenience method to do same 28 | # mutex.synchronize { shared_array << nil } 29 | end 30 | end 31 | end.each(&:join) 32 | 33 | # Here you can see that we have 10 * 10000 elements in array 34 | 35 | puts shared_array.size 36 | 37 | # Now all are same, because of mutex/ 38 | # The mutex sets up same boundaries for the thread. The first thread that hits 39 | # this bit of code will lock the mutex. it then becomes the owner of that mutex. 40 | # Until the owning thread unlocks the mutex, no other thread can lock it. 41 | 42 | # $ ruby => 10000 43 | # $ jruby => 10000 44 | # $ rbx => 1000 45 | -------------------------------------------------------------------------------- /gotchas/dont_be_so_sensitive.rb: -------------------------------------------------------------------------------- 1 | # Whitespace-insensitive? 2 | # NOT ALWAYS! 3 | # 4 | # With multiple args: 5 | # - No parens, no problem. 6 | # - Parens w/o space, OK. 7 | # - Parens and space, NO! 8 | # 9 | # Parser thinks it's an expression, as one arg, but (1, 2) is not a valid Ruby 10 | # expression! (All work fine w/ 1 arg.) 11 | 12 | def method(arg1, arg2); end 13 | 14 | method 1, 2 15 | # => nil 16 | 17 | method(1, 2) 18 | # => nil 19 | 20 | method (1, 2) 21 | # syntax error, unexpected ',', expecting ')' 22 | # method (1, 2) 23 | 24 | # "method /num" is an unended regex or string! Ruby thinks you are giving an 25 | # argument to method. General principle: use BALANCED whitespace; both sides or 26 | # neither. 27 | 28 | def method; 42; end 29 | num = 21 30 | method/num 31 | # => 2 32 | 33 | method / num 34 | # => 2 35 | 36 | method/ num 37 | # => 2 38 | 39 | method /num 40 | # SyntaxError: unterminated regexp 41 | 42 | # "one -2" makes Ruby think you are giving an argument (of -2) to method one. 43 | # (Same for +2 ... or even *2!) Again: use BALANCED whitespace; both sides or 44 | # neither. 45 | 46 | def one 47 | 1 48 | end 49 | 50 | one - 2 51 | # => -1 52 | 53 | one-2 54 | # => -1 55 | 56 | one- 2 57 | # => -1 58 | 59 | one -2 60 | # ArgumentError: wrong number of arguments (1 for 0) 61 | 62 | # "Stabby" lambdas (1.9+) 63 | # Parentheses optional 64 | # Space before/after args without parens, OK. 65 | # Space after parens, OK. 66 | # Again, space before parens, NO! 67 | # UPDATE: Fixed in 2.0! 68 | 69 | dbl = ->(x) { x * 2 } 70 | # => # 71 | 72 | dbl = ->x{ x * 2 } 73 | # => # 74 | 75 | dbl = -> x { x * 2 } 76 | # => # 77 | 78 | two = -> { 2 } 79 | # => # 80 | 81 | dbl = -> (x) { x * 2 } 82 | # Syntax error, unexpected tLPAREN_ARG, expecting keyword_do_LAMBDA or tLAMBEG 83 | 84 | two = -> () { 2 } 85 | # Same syntax error 86 | -------------------------------------------------------------------------------- /structures/binary_search_tree.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class BinarySearchTree < BinaryTree 9 | include SearchTreeMethods 10 | 11 | def initialize(*args) 12 | super 13 | end 14 | 15 | def find(obj) 16 | return nil if empty? 17 | diff = obj <=> @key 18 | if diff == 0 19 | return @key 20 | elsif diff < 0 21 | return @left.find(obj) 22 | elsif diff > 0 23 | return @right.find(obj) 24 | end 25 | end 26 | 27 | def min 28 | if empty? 29 | nil 30 | elsif @left.empty? 31 | @key 32 | else 33 | @left.min 34 | end 35 | end 36 | 37 | def insert(obj) 38 | if empty? 39 | attachKey(obj) 40 | else 41 | diff = obj <=> @key 42 | if diff == 0 43 | raise ArgumentError 44 | elsif diff < 0 45 | @left.insert(obj) 46 | elsif diff > 0 47 | @right.insert(obj) 48 | end 49 | end 50 | balance 51 | end 52 | 53 | def attachKey(obj) 54 | raise StateError unless empty? 55 | @key = obj 56 | @left = BinarySearchTree.new 57 | @right = BinarySearchTree.new 58 | end 59 | 60 | def balance; end 61 | 62 | def withdraw(obj) 63 | raise ArgumentError if empty? 64 | diff = obj <=> @key 65 | if diff == 0 66 | if !@left.empty? 67 | max = @left.max 68 | @key = max 69 | @left.withdraw(max) 70 | elsif !@right.empty? 71 | min = @right.min 72 | @key = min 73 | @right.withdraw(min) 74 | else 75 | detachKey 76 | end 77 | elsif diff < 0 78 | @left.withdraw(obj) 79 | elsif diff > 0 80 | @right.withdraw(obj) 81 | end 82 | balance 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /patterns/behavioral/interpreter.rb: -------------------------------------------------------------------------------- 1 | # This pattern provides an interpreter to deal with an abstract language. Using 2 | # classes we can understand the inputs for parse them 3 | 4 | # Parsing entity 5 | class Number 6 | attr_reader :value 7 | 8 | def initialize(value) 9 | @value = value 10 | end 11 | 12 | def execute 13 | value 14 | end 15 | end 16 | 17 | # Operation interface 18 | class Operation 19 | attr_reader :left, :right 20 | 21 | def initialize(left, right) 22 | @left = left 23 | @right = right 24 | end 25 | 26 | def execute 27 | raise NotImplementedError 28 | end 29 | end 30 | 31 | # Operations 32 | class Plus < Operation 33 | def execute 34 | left.execute + right.execute 35 | end 36 | end 37 | 38 | class Minus < Operation 39 | def execute 40 | left.execute - right.execute 41 | end 42 | end 43 | 44 | class Multiply < Operation 45 | def execute 46 | left.execute * right.execute 47 | end 48 | end 49 | 50 | class Devide < Operation 51 | def execute 52 | left.execute / right.execute 53 | end 54 | end 55 | 56 | # Interpreter class 57 | class Interpreter 58 | def self.parse(input) 59 | stack = [] 60 | 61 | input.lstrip! 62 | until input.empty? 63 | case input 64 | when /\A-?\d+(\.\d+)?/ 65 | stack << Number.new($&.to_i) 66 | else 67 | second = stack.pop 68 | first = stack.pop 69 | 70 | case input 71 | when /\A\+/ 72 | stack << Plus.new(first, second) 73 | when /\A\-/ 74 | stack << Minus.new(first, second) 75 | when /\A\*/ 76 | stack << Multiply.new(first, second) 77 | else 78 | raise SyntaxError 79 | end 80 | end 81 | 82 | input = $' 83 | input.lstrip! 84 | end 85 | 86 | raise SyntaxError unless stack.size == 1 87 | 88 | stack.first.execute 89 | end 90 | end 91 | 92 | # Usage 93 | puts Interpreter.parse('1 + 1 + 2 - 1') # => 3 94 | -------------------------------------------------------------------------------- /structures/ordered_list_as_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class OrderedListAsLinkedList < OrderedList 9 | def initialize 10 | super 11 | @linkedList = SinglyLinkedList.new 12 | end 13 | 14 | attr_reader :linkedList 15 | 16 | attr_accessor :count 17 | 18 | def insert(obj) 19 | @linkedList.append(obj) 20 | @count += 1 21 | end 22 | 23 | def [](offset) 24 | raise IndexError unless (0...@count) === offset 25 | ptr = @linkedList.head 26 | i = 0 27 | while (i < offset) && !ptr.nil? 28 | ptr = ptr.succ 29 | i += 1 30 | end 31 | ptr.datum 32 | end 33 | 34 | def member?(obj) 35 | ptr = @linkedList.head 36 | until ptr.nil? 37 | return true if ptr.datum.equal?(obj) 38 | ptr = ptr.succ 39 | end 40 | false 41 | end 42 | 43 | def find(arg) 44 | ptr = @linkedList.head 45 | until ptr.nil? 46 | return ptr.datum if ptr.datum == arg 47 | ptr = ptr.succ 48 | end 49 | nil 50 | end 51 | 52 | def withdraw(obj) 53 | raise ContainerEmpty if @count == 0 54 | @linkedList.extract(obj) 55 | @count -= 1 56 | end 57 | 58 | def findPosition(obj) 59 | ptr = @linkedList.head 60 | until ptr.nil? 61 | break if ptr.datum == obj 62 | ptr = ptr.succ 63 | end 64 | Cursor.new(self, ptr) 65 | end 66 | 67 | class Cursor 68 | def initialize(list, element) 69 | @list = list 70 | @element = element 71 | end 72 | 73 | def datum 74 | @element.datum 75 | end 76 | 77 | def insertAfter(obj) 78 | @element.insertAfter(obj) 79 | @list.count += 1 80 | end 81 | 82 | def withdraw 83 | @list.linkedList.extract(@element.datum) 84 | @list.count -= 1 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /patterns/structural/flyweight.rb: -------------------------------------------------------------------------------- 1 | # The Flyweight pattern describes how to share objects to allow their use at 2 | # fine granularity without prohibitive cost. 3 | 4 | # Flyweight Object 5 | class Lamp 6 | attr_reader :color 7 | 8 | def initialize(color) 9 | @color = color 10 | end 11 | end 12 | 13 | class TreeBranch 14 | attr_reader :branch_number 15 | 16 | def initialize(branch_number) 17 | @branch_number = branch_number 18 | end 19 | 20 | def hang(lamp) 21 | puts "Hang #{lamp.color} lamp on branch #{branch_number}" 22 | end 23 | end 24 | 25 | # Flyweight Factory 26 | class LampFactory 27 | attr_reader :lamps 28 | 29 | def initialize 30 | @lamps = {} 31 | end 32 | 33 | def find_lamp(color) 34 | if lamps.key?(color) 35 | lamp = lamps[color] 36 | else 37 | lamp = Lamp.new(color) 38 | lamps[color] = lamp 39 | end 40 | lamp 41 | end 42 | 43 | def total_number_of_lamps_made 44 | lamps.size 45 | end 46 | end 47 | 48 | class ChristmasTree 49 | attr_reader :lamp_factory, :lamps_hung 50 | 51 | def initialize(lamp_factory = LampFactory.new) 52 | @lamp_factory = lamp_factory 53 | @lamps_hung = 0 54 | 55 | dress_up_the_tree 56 | end 57 | 58 | def hang_lamp(color, branch_number) 59 | TreeBranch.new(branch_number).hang(lamp_factory.find_lamp(color)) 60 | lamps_hung += 1 61 | end 62 | 63 | def dress_up_the_tree 64 | yield self if block_given? 65 | end 66 | end 67 | 68 | # Usage 69 | christmas_tree = ChristmasTree.new 70 | christmas_tree.dress_up_the_tree do 71 | hang_lamp('red', 1) 72 | hang_lamp('blue', 1) 73 | hang_lamp('yellow', 1) 74 | hang_lamp('red', 2) 75 | hang_lamp('blue', 2) 76 | hang_lamp('yellow', 2) 77 | hang_lamp('red', 3) 78 | hang_lamp('blue', 3) 79 | hang_lamp('yellow', 3) 80 | hang_lamp('red', 4) 81 | hang_lamp('blue', 4) 82 | hang_lamp('yellow', 4) 83 | hang_lamp('red', 5) 84 | hang_lamp('blue', 5) 85 | hang_lamp('yellow', 5) 86 | hang_lamp('red', 6) 87 | hang_lamp('blue', 6) 88 | hang_lamp('yellow', 6) 89 | hang_lamp('red', 7) 90 | hang_lamp('blue', 7) 91 | hang_lamp('yellow', 7) 92 | end 93 | -------------------------------------------------------------------------------- /patterns/behavioral/mediator.rb: -------------------------------------------------------------------------------- 1 | # The essence of the Mediator Pattern is to "define an object that encapsulates 2 | # how a set of objects interact". It promotes loose coupling by keeping objects 3 | # from referring to each other explicitly, and it allows their interaction to be 4 | # varied independently. Client classes can use the mediator to send messages to 5 | # other clients, and can receive messages from other clients via an event on the 6 | # mediator class. 7 | 8 | # Classes witch interact with mediator 9 | class Buyer 10 | attr_accessor :balance 11 | 12 | def initialize(balance) 13 | @balance = balance 14 | end 15 | 16 | def use_agency_for_buying_house(agency) 17 | @agency = agency 18 | end 19 | 20 | def buy(house) 21 | @agency.purchase(house, self) 22 | end 23 | end 24 | 25 | class Seller 26 | attr_accessor :balance 27 | 28 | def initialize(balance = 0) 29 | @balance = balance 30 | end 31 | 32 | def use_agency_for_selling_house(agency, house) 33 | agency.register house 34 | end 35 | end 36 | 37 | class House 38 | attr_reader :price, :owner 39 | 40 | def initialize(price, owner) 41 | @price = price 42 | @owner = owner 43 | end 44 | 45 | def new_owner(buyer) 46 | @owner = buyer 47 | end 48 | end 49 | 50 | # Mediator class 51 | class Agency 52 | def initialize 53 | @houses = [] 54 | end 55 | 56 | def register(house) 57 | @houses << house 58 | end 59 | 60 | def purchase(house, buyer) 61 | if can_make_deal? house, buyer 62 | make_payment house, buyer 63 | change_owner house, buyer 64 | end 65 | end 66 | 67 | private 68 | 69 | def can_make_deal?(house, buyer) 70 | @houses.include?(house) && buyer.balance >= house.price 71 | end 72 | 73 | def make_payment(house, buyer) 74 | house.owner.balance += house.price 75 | buyer.balance -= house.price 76 | end 77 | 78 | def change_owner(house, buyer) 79 | house.new_owner buyer 80 | end 81 | end 82 | 83 | # Usage 84 | buyer = Buyer.new(1000) 85 | 86 | seller = Seller.new(0) 87 | house = House.new(100, seller) 88 | 89 | agency = Agency.new 90 | buyer.use_agency_for_buying_house agency 91 | seller.use_agency_for_selling_house agency, house 92 | 93 | buyer.buy house 94 | 95 | puts house.inspect 96 | -------------------------------------------------------------------------------- /solid/open_close.rb: -------------------------------------------------------------------------------- 1 | # The Open/Closed Principle states that classes or methods should be open for 2 | # extension, but closed for modification. This tells us we should strive for 3 | # modular designs that make it possible for us to change the behavior of the 4 | # system without making modifications to the classes themselves. This is 5 | # generally achieved through the use of patterns such as the strategy pattern. 6 | 7 | # In the below example we can see that we’ll have to modify our file parser 8 | # anytime we add a client that reports usage information to us in a different 9 | # file format. This violates the Open/Closed Principle. 10 | 11 | class FileParser 12 | attr_reader :file 13 | 14 | def initialize(file) 15 | @file = file 16 | end 17 | 18 | def parse 19 | # If we want add new parser we must to edit this method and in private method 20 | case file.format 21 | when :xml 22 | parse_xml 23 | when :cvs 24 | parse_cvs 25 | # when :json 26 | # parse_json 27 | end 28 | end 29 | 30 | private 31 | 32 | def parse_xml 33 | # Implementation 34 | end 35 | 36 | def parse_cvs 37 | # Implementation 38 | end 39 | 40 | # New parse method 41 | # def parse_json 42 | # # Implementation 43 | # end 44 | end 45 | 46 | # Solution 47 | 48 | # With this refactor we’ve made it possible to add new parsers without changing any code. Any additional behavior will only require the addition of a new handler. This makes our FileParser reusable and in many cases will keep us in compliance with the Single Responsibility Principle as well by encouraging us to create smaller more focused classes. 49 | 50 | class FileParser 51 | attr_reader :parser 52 | 53 | def initialize(parser) 54 | @parser = parser 55 | end 56 | 57 | def parse(file) 58 | # Now if we want new parser just write new Class and pass it to method 59 | Data.new(parser.parse(file)) 60 | end 61 | end 62 | 63 | class JsonParser 64 | # We write new class for extension solution. 65 | def self.parse(_file) 66 | # Implementation 67 | end 68 | end 69 | 70 | class XmlParser 71 | def self.parse(_file) 72 | # Implementation 73 | end 74 | end 75 | 76 | class CvsParser 77 | def self.parse(_file) 78 | # Implementation 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /gotchas/hoisting.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Variable hoisting in Ruby 3 | # Ruby has an interesting hositing mechanism built-in. Let’s take a dive and see 4 | # how it creates variables and do some experiments with it. Take this code for 5 | # example, and run it in console: 6 | 7 | puts x 8 | 9 | # We will get an error, obviously. x is not defined therefore you get an error: 10 | # NameError: undefined local variable or method `x' for main:Object. That is 11 | # normal. Next, try this: 12 | 13 | x = 1 if true 14 | puts x 15 | # => 1 16 | 17 | # Which is expected. Now, let’s try something else: 18 | 19 | x = 1 if false 20 | puts x 21 | # => ??? 22 | 23 | # What should this output? An error? Or nil? Or maybe 1, if somehow we live in a 24 | # parallel universe? If we try to run this code, we will get a nil. You don’t 25 | # believe me? Try adding this to the bottom of the script: 26 | 27 | puts x.class 28 | # => NilClass 29 | 30 | # You see, if we try to call an undefined variable we will get a NameError. But, 31 | # if we define the variable in a part of the code that will not be run at all we 32 | # will get a nil. 33 | 34 | # **Hoisting** 35 | # Well, it’s not that complicated really. But, it’s a quirk that most of us have 36 | # not ran across. The “magic” here is done by Ruby’s parser. Basically, when the 37 | # parser runs over the if-clause (look in weird_3.rb example file) it first 38 | # declares the variable, regardless of whether it will actually be executed. This 39 | # means that when the parser sees x=1, it will actually declare the variable, by 40 | # assigning it to nil and let the interpreter than figure out if the x = 1 line 41 | # will ever get executed. Don’t confuse the parser with the interpreter. The 42 | # parser does not care whether x ever gets a value. The job of the parser is just 43 | # to go over the code, find any local variables and allocate “space” for those 44 | # variables. More specifically, set them to nil. On the other hand, it’s the 45 | # interpreter that will follow the logical path of the program and see if / when 46 | # x will get a value and act on it. Last but not least, if you know about 47 | # hoisting in JavaScript, it’s worth mentioning that Ruby’s hoisting is much 48 | # different. In Ruby every variable is available only after the line where the 49 | # variable has been assigned, regardless if that line will be executed or not. 50 | -------------------------------------------------------------------------------- /solid/interface_segregation.rb: -------------------------------------------------------------------------------- 1 | # The principle states that a client should not be forced to depend on methods 2 | # that it does not use 3 | 4 | # In this example, there are Computer, Programmer and Technician classes. Both, 5 | # Programmer and Technician use the Computer in a different way. The programmer 6 | # uses the computer for typing, but the technician knows how to change the 7 | # computer hard drive. What Interface Segregation Principle (ISP) enforces is 8 | # that one class should not depend on methods it does not use. 9 | # In our case, Programmer is unnecessarily coupled to the 10 | # Computer#change_hard_drive method because it does not use it, but the state 11 | # changes that this method enforces can affect the Programmer. Let's refactor 12 | # the code to obey the LSP. 13 | 14 | class Computer 15 | def turn_on 16 | # Implementation 17 | end 18 | 19 | def type 20 | # Implementation 21 | end 22 | 23 | def change_hard_drive 24 | # Implementation 25 | end 26 | end 27 | 28 | class User 29 | attr_reader :computer 30 | 31 | def initialize(computer) 32 | @computer = computer 33 | end 34 | end 35 | 36 | class Programmer < User 37 | def use_computer 38 | computer.turn_on 39 | computer.type 40 | end 41 | end 42 | 43 | class Technician < User 44 | def fix_computer 45 | computer.change_hard_drive 46 | end 47 | end 48 | 49 | # Solution 50 | 51 | # After this refactor the Technician uses a different object from the type 52 | # ComputerInternals which is isolated from the state of the Computer. The state 53 | # of the Computer object can be influenced by the Programmer but the changes 54 | # wont affect the Technician in any way. 55 | 56 | class Computer 57 | def turn_on 58 | # Implementation 59 | end 60 | 61 | def type 62 | # Implementation 63 | end 64 | end 65 | 66 | class ComputerInternals 67 | def change_hard_drive 68 | # Implementation 69 | end 70 | end 71 | 72 | class Programmer 73 | attr_reader :computer 74 | 75 | def initialize(computer) 76 | @computer = computer 77 | end 78 | 79 | def use_computer 80 | computer.turn_on 81 | computer.type 82 | end 83 | end 84 | 85 | class Technician 86 | attr_reader :computer_internals 87 | 88 | def initialize(computer_internals) 89 | @computer_internals = computer_internals 90 | end 91 | 92 | def fix_computer 93 | computer_internals.change_hard_drive 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /gotchas/to_s_vs_to_str.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Concept: (The blockquote style does not look so well so I just pasted directly, 3 | # but these are all quoted from the links in the bottom of this page). You should 4 | # not implement `to_str` unless your object acts like a string, rather than just 5 | # having a string *representation*. The only core class that implements `to_str` 6 | # is String itself. [to_i and to_s] are not particularly strict: if an object has 7 | # some kind of decent representation as a string, for example, it will probably 8 | # have a to_s method… [to_int and to_str] are strict conversion functions: you 9 | # implement them only if you object can naturally be used every place a string or 10 | # an integer could be used. `to_str` is used by methods such as `String#concat` 11 | # to convert their arguments to a string. Unlike to_s, which is supported by 12 | # almost all classes, `to_str` is normally implemented only by those classes that 13 | # act like strings. Of the built-in classes, only `Exception` and `String` 14 | # implement `to_str`, `print`, `puts` and string interpolation(`#{}` syntax) use 15 | # `to_s`. Mostly everything else (`glob`, `split`, `match`, string concatenation) 16 | # uses `to_str`. 17 | 18 | # Simple Summary: `to_s` is defined on every object and will always return 19 | # something. `to_str` is only defined on objects that are string-like. For 20 | # example, `Symbol` has `to_str` but `Array` does not. Thus, you can use 21 | # `obj.respond_to?(:to_str)` instead of something like `obj.is_a?(String)` if you 22 | # want to take advantage of duck typing without worrying about whether the class 23 | # you're working with is a subclass of `String` or not. The documentation for 24 | # `Exception#to_str` reads: "By supplying a `to_str` method, exceptions are 25 | # agreeing to be used where `Strings` are expected." So it's all about 26 | # expectations. `to_s`: Give me a String no matter what! `to_str`: I am pretty 27 | # sure you are String-like. So sure, that I'd prefer to get a NoMethodException 28 | # in the case where you're not. Use case? 29 | # 30 | # Borrowed from Confident Ruby. 31 | 32 | class ArticleTitle 33 | def initialize(text) 34 | @text = text 35 | end 36 | 37 | def slug 38 | @text.strip.tr_s('^A-Za-z0-9', '-').downcase 39 | end 40 | 41 | def to_str 42 | puts 'to_str called' 43 | @text 44 | end 45 | end 46 | 47 | title = ArticleTitle.new('A Modest Proposal') 48 | 49 | # By defining #to_str, ArticleTitle can be used in places Ruby expects a String: 50 | 51 | puts 'Today’s Feature: ' + title 52 | # => "to_str called" 53 | # => "Today’s Feature: A Modest Proposal" 54 | -------------------------------------------------------------------------------- /structures/ordered_list_as_array.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class OrderedListAsArray < OrderedList 9 | def initialize(size = 0) 10 | super() 11 | @array = Array.new(size) 12 | end 13 | 14 | attr_reader :array 15 | 16 | attr_accessor :count 17 | 18 | def insert(obj) 19 | raise ContainerFull if @count == @array.length 20 | @array[@count] = obj 21 | @count += 1 22 | end 23 | 24 | def member?(obj) 25 | for i in 0...@count 26 | return true if @array[i].equal?(obj) 27 | end 28 | false 29 | end 30 | 31 | def find(obj) 32 | for i in 0...@count 33 | return @array[i] if @array[i] == obj 34 | end 35 | nil 36 | end 37 | 38 | def withdraw(obj) 39 | raise ContainerEmpty if @count == 0 40 | i = 0 41 | i += 1 while (i < @count) && !@array[i].equal?(obj) 42 | raise ArgumentError if i == @count 43 | while i < @count - 1 44 | @array[i] = @array[i + 1] 45 | i += 1 46 | end 47 | @array[i] = nil 48 | @count -= 1 49 | end 50 | 51 | class Cursor 52 | def initialize(list, offset) 53 | super() 54 | @list = list 55 | @offset = offset 56 | end 57 | 58 | def datum 59 | raise IndexError unless (0...@list.count) === offset 60 | @list[@offset] 61 | end 62 | end 63 | 64 | def findPosition(obj) 65 | i = 0 66 | i += 1 while (i < @count) && (@array[i] != obj) 67 | Cursor.new(self, i) 68 | end 69 | 70 | def [](offset) 71 | raise IndexError unless (0...@count) === offset 72 | @array[offset] 73 | end 74 | 75 | class Cursor < Cursor 76 | def insertAfter(obj) 77 | raise IndexError unless (0...@list.count) === @offset 78 | raise ContainerFull if @list.count == @list.array.length 79 | insertPosition = @offset + 1 80 | i = @list.count 81 | while i > insertPosition 82 | @list.array[i] = @list.array[i - 1] 83 | i -= 1 84 | end 85 | @list.array[insertPosition] = obj 86 | @list.count += 1 87 | end 88 | 89 | def withdraw 90 | raise IndexError unless (0...@list.count) === @offset 91 | raise ContainerEmpty if @list.count == 0 92 | i = @offset 93 | while i < @list.count - 1 94 | @list.array[i] = @list.array[i + 1] 95 | i += 1 96 | end 97 | @list.array[i] = nil 98 | @list.count -= 1 99 | end 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /gotchas/private_method.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Private methods 3 | # Remember how we said that instance variables store data that is “private” to the 4 | # object? Instance variables are only made accessible to the outside world (we say 5 | # “it is exposed”) if we add attribute accessors to the class. In the same way 6 | # classes sometimes want to keep certain methods private: methods that aren’t 7 | # supposed to be called from outside of the object. Only the object itself is 8 | # supposed to use them internally, from other methods. Imagine I am an instance 9 | # of a class ItalianRestaurant, and I have a method pizza, which is supposed to 10 | # return an instance of the class Pizza. When you approach me, and call the 11 | # method pizza (i.e. ask me to bring a pizza) then I’ll know what to do, and how 12 | # to do it. I’ll get some prepared pizza dough from somewhere, some tomato sauce, 13 | # vegetables and other stuff from somewhere else, prepare the pizza, bake it, put 14 | # it on a nice plate, and finally give (return) it to you. However, you don’t 15 | # really care about any of these details. You are hungry, and just want the pizza. 16 | # All the exact steps involved are something that I keep private to me, and maybe 17 | # they’ve been our family’s best kept secret for generations. This is pretty much 18 | # how objects work, too. The Italian restaurant object exposes some stuff to the 19 | # outer world (you), and keeps other things private. They’ll let you order a pizza, 20 | # and other things. But they won’t tell you the exact ingredients of their tomato 21 | # sauce, or how they manage to make this damn great pizza dough. In our Person 22 | # example it makes sense to make the method encrypt private. Currently, if you 23 | # run the following code it will execute just fine, even though it makes little 24 | # sense: 25 | 26 | person = Person.new('Ada') 27 | person.encrypt('some other secret') 28 | 29 | # Why would a person encrypt some arbitrary string for someone else, and return 30 | # it? This is something that the person object should keep private. The restaurant 31 | # wouldn’t turn flour, water, olive oil and other ingredients into pizza dough for 32 | # everyone else either. We can make the method encrypt private like so: 33 | 34 | module Encryption 35 | private 36 | 37 | def encrypt(string) 38 | Digest::SHA2.hexdigest(string) 39 | end 40 | end 41 | 42 | # The keyword private tells Ruby that all methods defined from now on, are 43 | # supposed to be private. They can be called from within the object (from other 44 | # methods that the class defines), but not from outside. If you now try to call 45 | # the method it will raise an error: 46 | 47 | person = Person.new('Ada') 48 | p person.encrypt('super secret') 49 | # private method `encrypt' called for # 50 | -------------------------------------------------------------------------------- /structures/singly_linked_list.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class SinglyLinkedList 9 | class Element 10 | attr_accessor :data, :succ 11 | 12 | def initialize(list, data, succ) 13 | @list = list 14 | @data = data 15 | @succ = succ 16 | end 17 | 18 | def insert_after(item) 19 | @succ = Element.new(@list, item, @succ) 20 | 21 | @list.tail = @succ if @list.tail.equal?(self) 22 | end 23 | 24 | def insert_before(item) 25 | tmp = Element.new(@list, item, self) 26 | 27 | if @list.head.equal?(self) 28 | @list.head = tmp 29 | else 30 | prevElem = @list.head 31 | while !prevElem.nil? && !prevElem.succ.equal?(self) 32 | prevElem = prevElem.succ 33 | end 34 | prevElem.succ = tmp 35 | end 36 | end 37 | end 38 | 39 | attr_accessor :head, :tail 40 | 41 | def initialize 42 | @head = nil 43 | @tail = nil 44 | end 45 | 46 | def purge 47 | @head = nil 48 | @tail = nil 49 | end 50 | 51 | def empty? 52 | @head.nil? 53 | end 54 | 55 | def first 56 | if @head.nil? 57 | raise Error 58 | else 59 | @head.data 60 | end 61 | end 62 | 63 | def last 64 | if @tail.nil? 65 | raise Error 66 | else 67 | @tail.data 68 | end 69 | end 70 | 71 | def prepend(elem) 72 | temp = Element.new(self, elem, @head) 73 | 74 | @tail = temp if @head.nil? 75 | @head = temp 76 | end 77 | 78 | def append(elem) 79 | temp = Element.new(self, elem, nil) 80 | 81 | if @head.nil? 82 | @head = temp 83 | else 84 | @tail.succ = temp 85 | end 86 | 87 | @tail = temp 88 | end 89 | 90 | def clone 91 | linked_list = SinglyLinkedList.new 92 | 93 | elem = @head 94 | until elem.nil? 95 | linked_list.append elem.data 96 | elem = @head.succ 97 | end 98 | 99 | linked_list 100 | end 101 | 102 | def extract 103 | elem = @head 104 | 105 | prevElem = nil 106 | while !elem.nil? && !elem.data.equal?(item) 107 | prevElem = elem 108 | elem = elem.succ 109 | end 110 | 111 | raise ArgumentError if elem.nil? 112 | 113 | if elem == @head 114 | @head = elem.succ 115 | else 116 | prevElem.succ = elem.succ 117 | end 118 | 119 | @tail = prevElem if elem == @tail 120 | end 121 | 122 | def each 123 | elem = @head 124 | until elem.nil? 125 | yield ptr.data 126 | elem = elem.succ 127 | end 128 | end 129 | end 130 | -------------------------------------------------------------------------------- /solid/single_responsibility.rb: -------------------------------------------------------------------------------- 1 | # The Single Responsibility Principle is the most abstract of the bunch. It 2 | # helps keep classes and methods small and maintainable. In addition to keeping 3 | # classes small and focused it also makes them easier to understand. 4 | 5 | # While we all agree that focusing on a single responsibility is important, it’s 6 | # difficult to determine what a class’s responsibility is. Generally, it is said 7 | # that anything that gives a class a reason to change can be viewed as a 8 | # responsibility. By change I am talking about structural changes to the class 9 | # itself (as in modifying the code in the class’s file, not the object’s 10 | # in-memory state). 11 | # In the below class we have a single command interface that processes 12 | # commission payments for deals. At first glance the class seems simple enough, 13 | # but let’s look at reasons we might want to change this class. Any change in 14 | # how we calculate commissions would require a change to this class. We could 15 | # introduce new commission rules or strategies that would cause our 16 | # calculate_commission method to change. For instance, we might want to vary 17 | # the percentage based on deal amount. Any change in the steps required to mark 18 | # a deal as processed in the mark_deal_processed method would result in a change 19 | # in the file as well. An example of this might be adding support for sending an 20 | # email summary of a specific person’s commissions after marking a deal 21 | # processed. The fact that we can identify multiple reasons to change signals a 22 | # violation of the Single Responsibility Principle. 23 | 24 | class DealProcessor 25 | attr_reader :deals 26 | 27 | def initialize(deals) 28 | @deals = deals 29 | end 30 | 31 | def process 32 | deals.each do |deal| 33 | # Here we calculate commission and create instance of Commission 34 | Commission.create(deal: deal, amount: calculate_commission(deal)) 35 | mark_deal_processed 36 | end 37 | end 38 | 39 | private 40 | 41 | def mark_deal_processed 42 | # Implementation 43 | end 44 | 45 | def calculate_commission(deal) 46 | deal.amount * 0.05 47 | end 48 | end 49 | 50 | class Commission 51 | # Implementation 52 | end 53 | 54 | # Solution 55 | 56 | class DealProcessor 57 | attr_reader :deals 58 | 59 | def initialize(deals) 60 | @deals = deals 61 | end 62 | 63 | def process 64 | deals.each do |deal| 65 | # Now we call calculator in one operation, all logic now in it 66 | CommissionCalculator.create_commission(deal) if mark_deal_processed 67 | end 68 | end 69 | 70 | private 71 | 72 | def mark_deal_processed 73 | # Implementation 74 | end 75 | end 76 | 77 | class CommissionCalculator 78 | def self.create_commission(deal) 79 | Commission.new(deal: deal, amount: calculate(deal)) 80 | end 81 | 82 | private 83 | 84 | def self.calculate(deal) 85 | deal.amount * 0.05 86 | end 87 | end 88 | 89 | class Commission 90 | # Implementation 91 | end 92 | -------------------------------------------------------------------------------- /threads/rails.rb: -------------------------------------------------------------------------------- 1 | # config.threadsafe!: what does it do? 2 | # Let’s take a look at the threadsafe! method: 3 | 4 | def threadsafe! 5 | @preload_frameworks = true 6 | @cache_classes = true 7 | @dependency_loading = false 8 | @allow_concurrency = true 9 | self 10 | end 11 | 12 | # Calling this method sets four options in our app configuration. Let’s walk 13 | # through each option and talk about what it does. 14 | 15 | # Preloading Frameworks 16 | # The first option @preload_frameworks does pretty much what it says, it forces 17 | # the Rails framework to be eagerly loaded on boot. When this option is not 18 | # enabled, framework classes are loaded lazily via autoload. In multi-threaded 19 | # environments, the framework needs to be eagerly loaded before any threads are 20 | # created because of thread safety issues with autoload. We know that loading 21 | # the framework isn’t threadsafe, so the strategy is to load it all up before 22 | # any threads are ready to handle requests. 23 | 24 | # Caching classes 25 | # The @cache_classes option controls whether or not classes get reloaded. 26 | # Remember when you’re doing “TDD” in your application? You modify a controller, 27 | # then reload the page to “test” it and see that things changed? Ya, that’s what 28 | # this option controls. When this option is false, as in development, your 29 | # classes will be reloaded when they are modified. Without this option, we 30 | # wouldn’t be able to do our “F5DD” (yes, that’s F5 Driven Development). 31 | # In production, we know that classes aren’t going to be modified on the fly, 32 | # so doing the work to figure out whether or not to reload classes is just 33 | # wasting resources, so it makes sense to never reload class definitions. 34 | 35 | # Dependency loading 36 | # This option, @dependency_loading controls code loading when missing constants 37 | # are encountered. For example, a controller references the User model, but the 38 | # User constant isn’t defined. In that case, if @dependency_loading is true, 39 | # Rails will find the file that contains the User constant, and load that file. 40 | # We already talked about how code loading is not thread safe, so the idea here 41 | # is that we should load the framework, then load all user code, then disable 42 | # dependency loading. Once dependency loading is disabled, framework code and 43 | # app code should be loaded, and any missing constants will just raise an 44 | # exception rather than attempt to load code. 45 | # We justify disabling this option in production because (as was mentioned 46 | # earlier) code loading is not threadsafe, and we expect to have all code loaded 47 | # before any threads can handle requests. 48 | 49 | # Allowing concurrency 50 | # @allow_concurrency option controls whether or not the Rack::Lock middleware 51 | # is used in your stack. Rack::Lock wraps a mutex around your request. The idea 52 | # being that if you have code that is not threadsafe, this mutex will prevent 53 | # multiple threads from executing your controller code at the same time. When 54 | # threadsafe! is set, this middleware is removed, and controller code can be 55 | # executed in parallel. 56 | -------------------------------------------------------------------------------- /patterns/behavioral/state.rb: -------------------------------------------------------------------------------- 1 | # Allow an object to alter its behavior when its internal state changes. 2 | # The object will appear to change its class. 3 | 4 | # Each call to state defines a new subclass of Connection that is stored in a 5 | # hash. Then, a call to transition_to instantiates one of these subclasses and 6 | # sets it to the be the active state. Method calls to Connection are delegated 7 | # to the active state object via method_missing 8 | 9 | module StatePattern 10 | class UnknownStateException < RuntimeError 11 | end 12 | 13 | def self.included(base) 14 | base.extend StatePattern::ClassMethods 15 | end 16 | 17 | module ClassMethods 18 | attr_reader :state_classes 19 | 20 | def state(state_name, &block) 21 | @state_classes ||= {} 22 | 23 | new_klass = Class.new(self, &block) 24 | new_klass.class_eval do 25 | alias_method :__old_init, :initialize 26 | 27 | def initialize(context, *args, &block) 28 | @context = context 29 | __old_init(*args, &block) 30 | end 31 | end 32 | 33 | @state_classes[state_name] = new_klass 34 | end 35 | end 36 | 37 | attr_accessor :current_state, :current_state_obj 38 | 39 | def transition_to(state_name, *args, &block) 40 | new_context = @context || self 41 | klass = new_context.class.state_classes[state_name] 42 | if klass 43 | new_context.current_state = state_name 44 | new_context.current_state_obj = klass.new(new_context, *args, &block) 45 | else 46 | raise UnknownStateException, "tried to transition to unknown state,#{state_name}" 47 | end 48 | end 49 | 50 | def method_missing(method, *args, &block) 51 | transition_to :initial unless @current_state_obj 52 | 53 | if @current_state_obj 54 | @current_state_obj.send(method, *args, &block) 55 | else 56 | super 57 | end 58 | end 59 | end 60 | 61 | class Connection 62 | include StatePattern 63 | 64 | # you always need a state named initial 65 | state :initial do 66 | def connect 67 | # move to state :connected. all other args to transition_to# are passed 68 | # to the new state's constructor transition_to:connected, "hello from 69 | # initial state" 70 | puts 'connected' 71 | end 72 | 73 | def disconnect 74 | puts 'not connected yet' 75 | end 76 | end 77 | 78 | state :connected do 79 | def initialize(msg) 80 | puts "initialize got msg:#{msg}" 81 | end 82 | 83 | def connect 84 | puts 'already connected' 85 | end 86 | 87 | def disconnect 88 | puts 'disconnecting' 89 | transition_to :initial 90 | end 91 | end 92 | 93 | def reset 94 | # you can also change the state from outside of the state objects 95 | # transition_to:initial 96 | puts 'resetting outside a state' 97 | end 98 | end 99 | 100 | # Usage 101 | c = Connection.new 102 | c.disconnect # => not connected yet 103 | c.connect # => connected, initialize got msg: hello from initial state 104 | c.connect # => already connected 105 | c.disconnect # => disconnecting 106 | c.connect # => connected, initialize got msg: hello from initial state 107 | c.reset # => reseting outside a state 108 | c.disconnect # => not connected yet 109 | -------------------------------------------------------------------------------- /structures/m_way_tree.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class MWayTree < SearchTree 9 | 10 | def initialize(m) 11 | assert { m > 2 } 12 | super() 13 | @key = Array.new(m - 1, 1) 14 | @subtree = Array.new(m) 15 | end 16 | 17 | def m 18 | @subtree.length 19 | end 20 | 21 | def depthFirstTraversal(&block) 22 | if not empty? 23 | for i in 1 .. @count 24 | yield (@key[i], PREVISIT) 25 | end 26 | @subtree[0].depthFirstTraversal(&block) 27 | for i in 1 .. @count 28 | yield (@key[i], INVISIT) 29 | @subtree[i].depthFirstTraversal(&block) 30 | end 31 | for i in 1 .. @count 32 | yield (@key[i], POSTVISIT) 33 | end 34 | end 35 | end 36 | 37 | def find(obj) 38 | return nil if empty? 39 | i = @count 40 | while i > 0 41 | diff = obj <=> @key[i] 42 | return @key[i] if diff == 0 43 | break if diff > 0 44 | i = i - 1 45 | end 46 | return @subtree[i].find(obj) 47 | end 48 | 49 | def findIndex(obj) 50 | return 0 if empty? or obj < @key[1] 51 | left = 1 52 | right = @count 53 | while left < right 54 | middle = (left + right + 1) / 2 55 | if obj < @key[middle] 56 | right = middle - 1 57 | else 58 | left = middle 59 | end 60 | end 61 | return left 62 | end 63 | 64 | def find(obj) 65 | return nil if empty? 66 | index = findIndex(obj) 67 | if index != 0 and @key[index] == obj 68 | return @key[index] 69 | else 70 | return @subtree[index].find(obj) 71 | end 72 | end 73 | 74 | def insert(obj) 75 | if empty? 76 | @subtree[0] = MWayTree.new(m) 77 | @key[1] = obj 78 | @subtree[1] = MWayTree.new(m) 79 | @count = 1 80 | else 81 | index = findIndex(obj) 82 | raise ValueError if index != 0 and @key[index] == obj 83 | if not full? 84 | i = @count 85 | while i > index 86 | @key[i + 1] = @key[i] 87 | @subtree[i + 1] = @subtree[i] 88 | i = i - 1 89 | end 90 | @key[index + 1] = obj 91 | @subtree[index + 1] = MWayTree.new(m) 92 | @count = @count + 1 93 | else 94 | @subtree[index].insert(obj) 95 | end 96 | end 97 | end 98 | 99 | def withdraw(obj) 100 | raise ArgumentError if empty? 101 | index = findIndex(obj) 102 | if index != 0 and @key[index] == obj 103 | if not @subtree[index - 1].empty? 104 | max = @subtree[index - 1].max 105 | @key[index] = max 106 | @subtree[index - 1].withdraw(max) 107 | elsif not @subtree[index].empty? 108 | min = @subtree[index].min 109 | @key[index] = min 110 | @subtree[index].withdraw(min) 111 | else 112 | @count = @count - 1 113 | i = index 114 | while i <= @count 115 | @key[i] = @key[i + 1] 116 | @subtree[i] = @subtree[i + 1] 117 | i = i + 1 118 | end 119 | @key[i] = nil 120 | @subtree[i] = nil 121 | if @count == 0 122 | @subtree[0] = nil 123 | end 124 | end 125 | else 126 | @subtree[index].withdraw(obj) 127 | end 128 | end 129 | 130 | end -------------------------------------------------------------------------------- /patterns/behavioral/chain_of_responsibility.rb: -------------------------------------------------------------------------------- 1 | # Avoid coupling the sender of a request to its receiver by giving more than one 2 | # object a chance to handle the request. Chain the receiving objects and pass 3 | # the request along the chain until an object handles it. 4 | 5 | # Implements the chain of responsibility pattern. Does not know anything 6 | # about the approval process, merely whether the current handler can approve 7 | # the request, or must pass it to a successor. 8 | class PurchaseApprover 9 | attr_reader :successor 10 | 11 | def initialize(successor) 12 | @successor = successor 13 | end 14 | 15 | def process_request(request) 16 | if approve_request request 17 | nil 18 | elsif successor 19 | successor.process_request request 20 | else 21 | deny_request request 22 | end 23 | end 24 | 25 | # This may be overridden by a handler if it wants to provide a custom action 26 | # when it is the last member of the chain 27 | def deny_request(request) 28 | # Implementation 29 | end 30 | end 31 | 32 | # Parts of approving chain 33 | # Base class for approvers who only consider whether the request amount is allowable 34 | class AmountApprover < PurchaseApprover 35 | BASE = 500 36 | 37 | def approve_request(request) 38 | if request.amount < self.class::ALLOWABLE 39 | print_approval request 40 | true 41 | else 42 | false 43 | end 44 | end 45 | end 46 | 47 | class Manager < AmountApprover 48 | ALLOWABLE = 10 * BASE 49 | 50 | def print_approval(request) 51 | puts "Manager will approve $#{request.amount}" 52 | end 53 | end 54 | 55 | class Director < AmountApprover 56 | ALLOWABLE = 20 * BASE 57 | 58 | def print_approval(request) 59 | puts "Director will approve $#{request.amount}" 60 | end 61 | end 62 | 63 | class VicePresident < AmountApprover 64 | ALLOWABLE = 40 * BASE 65 | 66 | def print_approval(request) 67 | puts "VicePresident will approve $#{request.amount}" 68 | end 69 | end 70 | 71 | class President < AmountApprover 72 | ALLOWABLE = 60 * BASE 73 | 74 | def print_approval(request) 75 | puts "President will approve $#{request.amount}" 76 | end 77 | end 78 | 79 | class ChiefFinancialOperations < PurchaseApprover 80 | # An example of a handler that does not inherit from AmountApprover 81 | def approve_request(request) 82 | if within_annual_budget? request 83 | puts "CFO will approve $#{request.amount}" 84 | true 85 | else 86 | false 87 | end 88 | end 89 | 90 | def within_annual_budget?(request) 91 | # Implementation 92 | end 93 | end 94 | 95 | class PurchaseRequest 96 | attr_reader :amount, :amount, :purpose 97 | 98 | def initialize(number, amount, purpose) 99 | @number = number 100 | @amount = amount 101 | @purpose = purpose 102 | end 103 | end 104 | 105 | # Realization of pattern class 106 | class CLP 107 | def initialize(*approvers) 108 | @authority = build_approvers(approvers).first 109 | end 110 | 111 | def process_request(request) 112 | @authority.process_request request 113 | end 114 | 115 | private 116 | 117 | def build_approvers(approver_classes) 118 | [].tap do |approvers| 119 | approver_classes.reverse.inject(nil) do |successor, approver| 120 | approver.new(successor).tap { |approver| approvers.unshift approver } 121 | end 122 | end 123 | end 124 | end 125 | 126 | # Usage 127 | approvers = CLP.new(Manager, Director, VicePresident, President) 128 | approvers.process_request PurchaseRequest.new(0, amount, 'General') 129 | -------------------------------------------------------------------------------- /patterns/structural/proxy.rb: -------------------------------------------------------------------------------- 1 | # When building a proxy, we could implement a method for each method in the 2 | # underlying object. However, this leads to a lot of repeated code and tightly 3 | # couples the proxy with the underlying object. A better alternative is to pass 4 | # method calls direcly to the underlying object. Ruby includes a method that is 5 | # perfect for this situation called method_missing. 6 | 7 | # Object witch is decorated 8 | class BankAccount 9 | attr_reader :balance 10 | 11 | def initialize(balance = 0) 12 | @balance = balance 13 | end 14 | 15 | def deposit(amount) 16 | balance += amount 17 | end 18 | 19 | def withdraw(amount) 20 | balance -= amount 21 | end 22 | end 23 | 24 | # Proxy 25 | # Do not use attr_reader for accessing to proxy object 26 | class BankAccountProxy 27 | def initialize(real_object) 28 | @real_object = real_object 29 | end 30 | 31 | def balance 32 | @real_object.balance 33 | end 34 | 35 | def deposit(amount) 36 | @real_object.deposit(amount) 37 | end 38 | 39 | def withdraw(amount) 40 | @real_object.withdraw(amount) 41 | end 42 | end 43 | 44 | # Protection proxy 45 | # Are you working on an MNC? If so, we might be well aware of the proxy server 46 | # that provides us internet by restricting access to some sort of websites like 47 | # public e-mail, social networking, data storage etc. The management feels that, 48 | # it is better to block some content and provide only work related web pages. 49 | # Proxy server does that job. This is a type of proxy design pattern 50 | class BankAccountProtectionProxy 51 | def initialize(real_account, owner_name) 52 | @subject = real_account 53 | @owner_name = owner_name 54 | end 55 | 56 | def deposit(amount) 57 | check_access 58 | @subject.deposit(amount) 59 | end 60 | 61 | def withdraw(amount) 62 | check_access 63 | @subject.withdraw(amount) 64 | end 65 | 66 | def balance 67 | check_access 68 | @subject.balance 69 | end 70 | 71 | def check_access 72 | # Implementation 73 | end 74 | end 75 | 76 | # Virtual proxy 77 | # In place of a complex or heavy object, use a skeleton representation. When an 78 | # underlying image is huge in size, just represent it using a virtual proxy 79 | # object and on demand load the real object. You know that the real object is 80 | # expensive in terms of instantiation and so without the real need we are not 81 | # going to use the real object. Until the need arises we will use the virtual proxy. 82 | class BankAccountVirtualProxy 83 | def initialize(&creation_block) 84 | @creation_block = creation_block 85 | end 86 | 87 | def deposit(amount) 88 | subject.deposit(amount) 89 | end 90 | 91 | def withdraw(amount) 92 | subject.withdraw(amount) 93 | end 94 | 95 | def balance 96 | subject.balance 97 | end 98 | 99 | def subject 100 | @subject ||= @creation_block.call 101 | end 102 | end 103 | 104 | # Remote proxy 105 | # In distributed object communication, a local object represents a remote object 106 | # (one that belongs to a different address space). The local object is a proxy 107 | # for the remote object, and method invocation on the local object results in 108 | # remote method invocation on the remote object. Think of an ATM implementation, 109 | # it will hold proxy objects for bank information that exists in the remote server. 110 | 111 | # Server part 112 | class BankAccountRemoteProxy 113 | # implementation 114 | end 115 | 116 | URI = 'druby://path_to_domain:port' 117 | DRb.start_service(URI, BankAccountRemoteProxy.new) 118 | DRb.thread.join 119 | 120 | # Client part 121 | URI = 'druby://path_to_domain:port' 122 | proxy = DRbObject.new_with_uri(URI) 123 | proxy.balance 124 | -------------------------------------------------------------------------------- /structures/axioms.rb: -------------------------------------------------------------------------------- 1 | # 1 - The time required to fetch a reference to an object from memory is a constant, 2 | # T_fetch, and the time required to store a reference to an object in memory is 3 | # a constant, T_store 4 | 5 | y = x 6 | 7 | # According to Axiom, the assignment statement has running time T_fetch + T_store. 8 | # That is, the time taken to fetch the object reference from variable x is 9 | # T_fetch and the time taken to store that object reference in the variable y is 10 | # T_store. 11 | 12 | y = 1 13 | 14 | # Also has running time T_fetch + T_store. To see why this should be the case, 15 | # consider that the constant '1' names a Fixnum object with value one. Therefore, 16 | # we can expect the cost of fetching the reference to the object named 1 is the 17 | # same as that of fetching a reference to any other object. 18 | 19 | # 2 - The times required to perform elementary arithmetic operations, such as 20 | # addition, subtraction, multiplication, division, and comparison, are all 21 | # constants. These times are denoted by T_+, T_-, T_/, T_*, and T_<, respectively. 22 | 23 | y += 1 24 | 25 | # We can determine time of a statement like is 2 * T_fetch + T_+ + T_store. This 26 | # is because we need to fetch two object references from the variables y and 1; 27 | # perform the addition giving a new object whose value is the sum; and, store 28 | # a reference to the new object in the variable y. 29 | # Ruby syntax provides an alternative way to express the same computation: 30 | 31 | y += 1 32 | 33 | # We shall assume that the alternative requires exactly the same running time as 34 | # the original statement. 35 | 36 | # 3 - The time required to call a method is a constant, T_call, and the time 37 | # required to return from a method is a constant, T_return 38 | # The rationale for making the overhead associated with parameter passing the 39 | # same as the time to store an object reference is that the passing of an 40 | # argument is conceptually the same as assignment of the actual parameter value 41 | # to the formal parameter of the method. 42 | 43 | y = f(x) 44 | 45 | # According to Axiom, the running time of the statement would be 46 | # T_fetch + 2 * T_store + T_call + T_f(x), where T_f(x) is the running time of 47 | # method f for input x. The first of the two stores is due to the passing of the 48 | # parameter x to the method f; the second arises from the assignment to the variable y. 49 | 50 | # 4 - The time required for the address calculation implied by an array 51 | # subscripting operation, e.g., a[i], is a constant, T_[]. This time does not 52 | # include the time to compute the subscript expression, nor does it include the 53 | # time to access the array element. 54 | 55 | y = a[i] 56 | 57 | # This is 3 * T_fetch Three operand fetches are required: the first to fetch a 58 | # reference to the array object a; the second to fetch a reference to the index 59 | # object i; and, the third to fetch a reference to the array element a[i]. 60 | 61 | # 5 - The time required to create a new object instance of a class is a constant, 62 | # T_new. This time does not include any time taken to initialize the object. 63 | # By applying Axioms we can determine that the running time of the statement 64 | 65 | i = Integer.new(0) 66 | 67 | # is T_new + T_fetch + 2 * T_store + T_call + T_fixnum_init, where T_fixnum_init 68 | # is the running time of the initialize method of the class Fixnum. 69 | 70 | # Example - In this section we apply Axioms, the analysis of the running time of a 71 | # program to compute the following simple arithmetic series summation 72 | 73 | def sum(n) 74 | result = 0 # T_fetch + T_store 75 | i = 1 # T_fetch + T_store 76 | while i <= n # (2 * T_fetch + T_<) * (n - 1) 77 | result += i # (2 * T_fetch + T_+ + T_store) * n 78 | i += 1 # (2 * T_fetch + T_+ + T_store) * n 79 | end 80 | result # T_return + T_fetch 81 | end 82 | 83 | # Total: (6 * T_fetch + 2 * T_store + T_< + 2 * T_+) * n + (5 * T_fetch + 2 * T_store + T_< + T_return) 84 | # T(n) = t1 * n + t2 85 | -------------------------------------------------------------------------------- /structures/tree.rb: -------------------------------------------------------------------------------- 1 | # This file contains the Ruby code from book of 2 | # "Data Structures and Algorithms 3 | # with Object-Oriented Design Patterns in Ruby" 4 | # by Bruno R. Preiss. 5 | # 6 | # Copyright (c) 2004 by Bruno R. Preiss, P.Eng. All rights reserved. 7 | 8 | class PrePostVisitor < Visitor 9 | 10 | abstractmethod :preVisit 11 | 12 | abstractmethod :inVisit 13 | 14 | abstractmethod :postVisit 15 | 16 | alias_method :visit, :inVisit 17 | 18 | end 19 | 20 | class PreOrder < PrePostVisitor 21 | 22 | def initialize(visitor) 23 | super() 24 | @visitor = visitor 25 | end 26 | 27 | def preVisit(obj) 28 | @visitor.visit(obj) 29 | end 30 | 31 | def inVisit(obj) 32 | end 33 | 34 | def postVisit(obj) 35 | end 36 | 37 | def done? 38 | @visitor.done? 39 | end 40 | 41 | end 42 | 43 | class InOrder < PrePostVisitor 44 | 45 | def initialize(visitor) 46 | super() 47 | @visitor = visitor 48 | end 49 | 50 | def preVisit(obj) 51 | end 52 | 53 | def inVisit(obj) 54 | @visitor.visit(obj) 55 | end 56 | 57 | def postVisit(obj) 58 | end 59 | 60 | def done? 61 | @visitor.done? 62 | end 63 | end 64 | 65 | class PostOrder < PrePostVisitor 66 | 67 | def initialize(visitor) 68 | super() 69 | @visitor = visitor 70 | end 71 | 72 | def preVisit(obj) 73 | end 74 | 75 | def inVisit(obj) 76 | end 77 | 78 | def postVisit(obj) 79 | @visitor.visit(obj) 80 | end 81 | 82 | def done? 83 | @visitor.done? 84 | end 85 | end 86 | 87 | class Tree < Container 88 | 89 | def initialize 90 | super 91 | end 92 | 93 | abstractmethod :key 94 | 95 | abstractmethod :getSubtree 96 | 97 | abstractmethod :empty? 98 | 99 | abstractmethod :leaf? 100 | 101 | abstractmethod :degree 102 | 103 | abstractmethod :height 104 | 105 | 106 | PREVISIT = -1 107 | INVISIT = 0 108 | POSTVISIT = +1 109 | 110 | def depthFirstTraversal(&block) 111 | if not empty? 112 | yield (key, PREVISIT) 113 | for i in 0 ... degree 114 | getSubtree(i).depthFirstTraversal(&block) 115 | end 116 | yield (key, POSTVISIT) 117 | end 118 | end 119 | 120 | def depthFirstTraversalAccept(visitor) 121 | assert { visitor.is_a?(PrePostVisitor) } 122 | depthFirstTraversal do |obj, mode| 123 | break if visitor.done? 124 | case mode 125 | when PREVISIT 126 | visitor.preVisit(obj) 127 | when INVISIT 128 | visitor.inVisit(obj) 129 | when POSTVISIT 130 | visitor.postVisit(obj) 131 | end 132 | end 133 | end 134 | 135 | def breadthFirstTraversal 136 | queue = QueueAsLinkedList.new 137 | queue.enqueue(self) if not empty? 138 | while not queue.empty? 139 | head = queue.dequeue 140 | yield head.key 141 | for i in 0 ... head.degree 142 | child = head.getSubtree(i) 143 | queue.enqueue(child) if not child.empty? 144 | end 145 | end 146 | end 147 | 148 | def breadthFirstTraversalAccept(visitor) 149 | breadthFirstTraversal do |obj| 150 | break if visitor.done? 151 | visitor.visit(obj) 152 | end 153 | end 154 | 155 | def each(&block) 156 | depthFirstTraversal do |obj, mode| 157 | block.call(obj) if mode == PREVISIT 158 | end 159 | end 160 | 161 | 162 | class Iterator < Opus8::Iterator 163 | 164 | def initialize(tree) 165 | @stack = StackAsLinkedList.new 166 | @stack.push(tree) if not tree.empty? 167 | end 168 | 169 | def more? 170 | not @stack.empty? 171 | end 172 | 173 | def succ 174 | if more? 175 | top = @stack.pop 176 | i = top.degree - 1 177 | while i >= 0 178 | subtree = top.getSubtree(i) 179 | @stack.push(subtree) if not subtree.empty? 180 | i -= 1 181 | end 182 | result = top.key 183 | else 184 | result = nil 185 | end 186 | return result 187 | end 188 | 189 | end 190 | 191 | def iter 192 | Iterator.new(self) 193 | end 194 | 195 | end -------------------------------------------------------------------------------- /gotchas/diff_proc_block_lambda.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # What Is the Difference Between a Block, a Proc, and a Lambda in Ruby? 3 | # 4 | # Block Examples: 5 | # Block is in between the curly braces 6 | 7 | [1, 2, 3].each { |x| puts x * 2 } 8 | 9 | # Block is everything between the do and end 10 | 11 | [1, 2, 3].each do |x| 12 | puts x * 2 13 | end 14 | 15 | # Proc Examples: 16 | # The '&' tells ruby to turn the proc into a block 17 | 18 | p = proc { |x| puts x * 2 } 19 | [1, 2, 3].each(&p) 20 | 21 | proc = proc { puts 'Hello World' } 22 | proc.call 23 | 24 | # Lambda Examples: 25 | 26 | lam = -> (x) { puts x * 2 } 27 | [1, 2, 3].each(&lam) 28 | lam = -> { puts 'Hello World' } 29 | lam.call 30 | 31 | # While it looks like these are all very similar, there are subtle differences 32 | # that I will cover below. Differences between Blocks and Procs. Procs are 33 | # objects, blocks are not. A proc (notice the lowercase p) is an instance of 34 | # the Proc class. 35 | 36 | p = proc { puts 'Hello World' } 37 | # This lets us call methods on it and assign it to variables. Procs can also 38 | # return themselves. 39 | 40 | p.call 41 | # => 'Hello World' 42 | 43 | p.class 44 | # => Proc 45 | 46 | a = p 47 | # a now equals p, a Proc instance 48 | 49 | p 50 | # => '#' 51 | 52 | # In contrast, a block is just part of the *syntax* of a method call. It doesn’t 53 | # mean anything on a standalone basis and can only appear in argument lists. 54 | 55 | { puts "Hello World"} 56 | # Syntax error 57 | 58 | a = { puts "Hello World"} 59 | # Syntax error 60 | 61 | # Only works as part of the syntax of a method call 62 | [1, 2, 3].each { |x| puts x * 2 } 63 | 64 | # At most one block can appear in an argument list In contrast, you can pass 65 | # multiple procs to methods. 66 | 67 | def multiple_procs(proc1, proc2) 68 | proc1.call 69 | proc2.call 70 | end 71 | 72 | a = proc { puts 'First proc' } 73 | b = proc { puts 'Second proc' } 74 | multiple_procs(a, b) 75 | 76 | # Differences between Procs and Lambdas. Before I get into the differences 77 | # between procs and lambdas, it is important to mention that they are both Proc 78 | # objects. 79 | 80 | proc = proc { puts 'Hello world' } 81 | lam = -> { puts 'Hello World' } 82 | 83 | proc.class 84 | # => Proc 85 | 86 | lam.class 87 | # => Proc 88 | 89 | # However, lambdas are a different ‘flavor’ of procs. This slight difference is 90 | # shown when returning the objects. 91 | 92 | proc 93 | # => '#' 94 | 95 | lam 96 | # => '' 97 | 98 | # The (lambda) notation is a reminder that while procs and lambdas are very 99 | # similar, even both instances of the Proc class, they are also slightly 100 | # different. Below are the key differences. 101 | 102 | # Lambdas check the number of arguments, while procs do not 103 | 104 | lam = ->(x) { puts x } 105 | # Creates a lambda that takes 1 argument 106 | 107 | lam.call(2) 108 | # => 2 109 | 110 | lam.call 111 | # ArgumentError: wrong number of arguments (0 for 1) 112 | 113 | lam.call(1, 2, 3) 114 | # ArgumentError: wrong number of arguments (3 for 1) 115 | 116 | # In contrast, procs don’t care if they are passed the wrong number of arguments. 117 | 118 | proc = proc { |x| puts x } 119 | # creates a proc that takes 1 argument 120 | 121 | proc.call(2) 122 | # => 2 123 | 124 | proc.call 125 | # => nil 126 | 127 | proc.call(1, 2, 3) 128 | # => 1 129 | # And forgets about the extra arguments 130 | 131 | # As shown above, procs don’t freak out and raise errors if they are passed the 132 | # wrong number of arguments. If the proc requires an argument but no argument is 133 | # passed then the proc returns nil. If too many arguments are passed than it 134 | # ignores the extra arguments. 135 | 136 | # Lambdas and procs treat the ‘return’ keyword differently return inside of a 137 | # lambda triggers the code right outside of the lambda code 138 | 139 | def lambda_test 140 | lam = -> { return } 141 | lam.call 142 | puts 'Hello world' 143 | end 144 | 145 | lambda_test 146 | # calling lambda_test prints 'Hello World' 147 | 148 | # ‘return’ inside of a proc triggers the code outside of the method where the 149 | # proc is being executed 150 | 151 | def proc_test 152 | proc = proc { return } 153 | proc.call 154 | puts 'Hello world' 155 | end 156 | 157 | proc_test 158 | # Calling proc_test prints nothing 159 | 160 | # Summary Differences 161 | # - Procs are objects, blocks are not 162 | # - At most one block can appear in an argument list 163 | # - Lambdas check the number of arguments, while procs do not 164 | # - Lambdas and procs treat the ‘return’ keyword differently 165 | -------------------------------------------------------------------------------- /gotchas/instance_eval.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | # Instance_eval with access to outside scope 3 | # In Ruby, it’s always been a minor annoyance to me that when you’re working with 4 | # DSL code, you have to choose between losing access to the surrounding scope 5 | # (implemented using `instance_eval`), or prefixing every call with a local 6 | # variable (implemented using `yield self`). Turns out there is a way to get the 7 | # best of both. Which works well, almost all the time. And ends in two rather 8 | # unexpected places: one is a really odd error; and the other is CoffeeScript 9 | # style function definition/call syntax. Sortof. 10 | 11 | class DslObject 12 | def initialize(&block) 13 | evaluate &block if block_given? 14 | end 15 | 16 | def evaluate(&block) 17 | case block.arity 18 | when 0 19 | instance_eval &block 20 | when 1 21 | yield self 22 | else 23 | raise 'Too many args for block' 24 | end 25 | end 26 | 27 | def do_something_useful(rhs) 28 | puts rhs 29 | end 30 | end 31 | 32 | # The `instance_eval` vs `yield(self)` issue is well known. So this section is 33 | # for you if you’re not already clear on that. Use `yield self` and you have to 34 | # prefix every call with a block variable: 35 | 36 | class Other 37 | def surname 38 | 'de la Grace' 39 | end 40 | 41 | def yld 42 | DslObject.new do |dsl| 43 | dsl.do_something_useful surname 44 | end 45 | end 46 | end 47 | 48 | Other.new.yld 49 | # => 'de la Grace' 50 | # => # 51 | 52 | # But prefixing every call with a local variable becomes painful in some cases, 53 | # for example all the `t.` in an ActiveRecord migration. But in order to make the 54 | # prefix unnecessary you have to use `instance_eval`, and then code inside the 55 | # block can’t access methods defined outside the block: 56 | 57 | class Other 58 | def surname 59 | 'de la Grace' 60 | end 61 | 62 | def inst 63 | DslObject.new do 64 | do_something_useful surname 65 | end 66 | end 67 | end 68 | 69 | Other.new.inst 70 | # => undefined local variable or method `surname' for # (NameError) 71 | 72 | # Which is quite a severe limitation. The Solution is use a delegator that knows 73 | # about both the binding for the block, and the dsl object, and can send method 74 | # calls to the right place. 75 | 76 | class Combinder < BasicObject 77 | def initialize(obj, saved_binding) 78 | @obj = obj 79 | @saved_binding = saved_binding 80 | end 81 | 82 | def __bound_self__ 83 | @saved_binding.eval('self') 84 | end 85 | 86 | def method_missing(meth, *args, &blk) 87 | # methods in dsl object are called in preference to self outside the block 88 | if @obj.respond_to?(meth) 89 | # dsl method, so call it 90 | @obj.send meth, *args, &blk 91 | else 92 | __bound_self__.send meth, *args, &blk 93 | end 94 | end 95 | 96 | def respond_to_missing?(meth, _include_all) 97 | __bound_self__.respond_to?(meth) || @obj.respond_to?(meth) 98 | end 99 | end 100 | 101 | class DslObject 102 | def initialize(&block) 103 | evaluate &block if block_given? 104 | end 105 | 106 | def evaluate(&block) 107 | case block.arity 108 | when 0 109 | Combinder.new(self, block.binding).instance_eval &block 110 | when 1 111 | yield self 112 | else 113 | raise 'Too many args for block' 114 | end 115 | end 116 | 117 | def do_something_useful(rhs) 118 | puts rhs 119 | end 120 | end 121 | 122 | # Now Other.new.inst will work. The Problem with The Solution 123 | 124 | class Oops < Other 125 | def takes_args(*args) 126 | args.join('_') 127 | end 128 | 129 | def inst 130 | takes_args = 'four_fice' 131 | DslObject.new do 132 | do_something_useful surname 133 | do_something_useful(takes_args % w[one two three]) 134 | end 135 | end 136 | end 137 | 138 | Oops.new.inst 139 | # => 'de la Grace' 140 | 141 | # NoMethodError: undefined method `w' for # from 142 | # combinder.rb:16:in `method_missing'. This is caused by the way the ruby 143 | # interpreter distinguishes between a method call and a local variable. In this 144 | # case, the local variable `takes_args` is in the binding for the block, so it’s 145 | # not treated as a method call. And because that happens in the interpreter, 146 | # there’s no way to hook into it and produce a more meaningful error message. 147 | # Aside: % is being treated as the sprintf shortcut, and w[one two three] is not 148 | # syntactically correct. Unless w one two and three were defined. And I’ve seen 149 | # another unexpected syntax error in this situation, when passing a literal symbol. 150 | # Because : has other meanings in Ruby. Of course, if you said 151 | # takes_args(%w[one two three]) it would all work fine because the `(...)` marks 152 | # `takes_args` as a method call, and there’s no ambiguity with the local variable, 153 | # so it ends up in `Combinder#method_missing`. Another workaround is to define 154 | # methods in `Combinder` like this: 155 | 156 | class Combinder 157 | def __outside__ 158 | __bound_self__ 159 | end 160 | 161 | def __inside__ 162 | # This is a bit harder than __outside__, but can be done 163 | end 164 | end 165 | 166 | # Which would allow explicit access to the binding (__outside__) and the dsl object 167 | # (__inside__), and those could be used to resolve ambiguous naming. Squeel has 168 | # my { } which similarly gets through the instance_eval block boundary. So seeing 169 | # as there are at least 3 workarounds, my opinion is that the weirdness of the error 170 | # message is the biggest drawback. 171 | 172 | # The CoffeeScript connection: This part I discovered by accident. In Combinder 173 | # I had some code for accessing the local variables in the binding. This code turned 174 | # out to be unnecessary because ruby already accesses those. But that code sparked 175 | # off a realisation that since a method call can be ‘forced’ using (), it would be 176 | # possible in Combinder#method_missing to check if there was a callable object with 177 | # that name (ie respond_to?( :call ) == true), and call it. Resulting in something 178 | # like this: 179 | 180 | fn = ->(*args) { puts "fn gives you: #{args.inspect}" } 181 | 182 | functionaliser do 183 | fn(%w[coffee script style]) 184 | end 185 | 186 | fn gives you: %w[coffee script style] 187 | # => # 188 | 189 | # So the block inserts indirection into the resolution of names so that it’s 190 | # possible to treat Procs as methods. I didn’t go any further down that rabbit hole, 191 | # mainly because right now I don’t have any sensible use cases for something like that. 192 | --------------------------------------------------------------------------------