Skip to content

helabed/mastering-ruby

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mastering-ruby

my attempt at mastering ruby motivated by Dave Thomas video tutorials https://pragprog.com/screencasts/v-dtrubyom/the-ruby-object-model-and-metaprogramming (no longer available - as of August, 2020)

rspec ./*_spec.rb -f doc
/Users/helabed/rails-proj/mastering-ruby/enumerable_predicates_spec.rb:3: warning: already initialized constant Enumerable_methods
/Users/helabed/rails-proj/mastering-ruby/enumerable_filters_spec.rb:3: warning: previous definition of Enumerable_methods was here
/Users/helabed/rails-proj/mastering-ruby/enumerable_transformers_spec.rb:9: warning: already initialized constant Enumerable_methods
/Users/helabed/rails-proj/mastering-ruby/enumerable_predicates_spec.rb:3: warning: previous definition of Enumerable_methods was here

BinarySearchTree (BST) Specification
  Traversal and Search
    the first element should be the root
    should be able to display all elements in order
    should be able to display all elements pre order
    should be able to display all elements post order
    should be able to search and locate an element
    should be able to display height of the tree
    should be able to display all leaf elements right to left
    should be able to display all ancestor elements from root to a certain node
    should be able to display all descendant elements from a certain node and to all its leaves
  deleting elements from the BST
    should be able to delete a right leaf element
    should be able to delete a left leaf element
    should be able to delete an element with 2 children
    should be able to delete an element with 1 child
    should be able to delete the root element

BinarySearchTree (BST) testing - iteration last

/Users/helabed/rails-proj/mastering-ruby/binary_search_tree_w_new_display_w_levels_spec.rb:39: warning: already initialized constant L
/Users/helabed/rails-proj/mastering-ruby/binary_search_tree_spec.rb:20: warning: previous definition of L was here
  should search for an element stored in BST

blocks, Procs and lambdas.
  many ways to define/create an object out of a block.
    can be created with a lambda
    can be created with Proc.new
    can be created when passed as a method parameter with an '&' prepended - internally same as Proc.new
    can be created with the stabby proc operator -> and called just like Proc.new.call is called
    can be created with the stabby proc operator -> and called using the square bracket syntax
    and a deprecated way to do it, is same as lambda in Ruby 1.8, and Proc.new in Ruby 1.9
      can be created with a with a call to the built in method 'proc' - avoid using 'proc'
  Proc.new is liberal in dealing with parameters passing - behaves like parallel assignemnt.
    runs fine when passed the exact number of arguments as it was defined
    runs fine when passed the exact number of string arguments as it was defined
    runs fine when passed a smaller number of arguments as it was defined
    runs fine when passed a larger number of arguments as it was defined
    runs fine when passed a larger number of arguments and when it is defined with a variable argument list
  lambda is restrictive in dealing with parameters passing - behaves like a method call.
    runs fine when passed the exact number of arguments as it was defined
    runs fine when passed the exact number of string arguments as it was defined
    raises an error when passed a smaller number of arguments as it was defined
    raises an error when passed a larger number of arguments as it was defined
    runs fine when passed a larger number of arguments and when it is defined with a variable argument list
  a 'return' statement from Proc.new exists the surrounding context - behaves like inline code.
    should exit surrounding method and should not reach bottom of said method
    because a block passed to a method such as collection.each is always converted to a Proc.new
      should exit the surrounding  method as expected when code inside a block hits a return statement
  a 'return' statement from lambda exists the proc only - lambda behaves like a method.
    should exit the block only and execution should reach bottom of surrounding method
  a 'binding' is the context in which code is executing.
    should provide access to all variables in this binding including all passed parameters, instance variables, and local variables
    should provide access to all associated block in this binding
    should provide access to 'self' in this binding
    Ruby provides a binding with every block it creates when using 'lambda'
    Ruby provides a binding with every block it creates when using 'Proc.new'
  a 'reference' to the block can be passed around with an '&'
    then the block can be called with the call method

Struct
  a Struct allows bundling a number of attributes together without needing an explicit class
    a struct member is either a quoted string ("name") or a Symbol (:name)
      should create a reader and writer method for each member declared in Struct.new
  the Struct class generates new subclasses that hold a set of members and their values
    should confirm that all classes created using Struct are indeed all its subclasses

class_eval or module_eval
  allows us to execute any code inside a block where the receiver is a class or a module
    should execte any code where the receiver is a class
    should execte any code where the receiver is a module
  allows us to define methods inside a block
    should allow us to define instance methods on the class itself
    everytime we call class_eval on a class, ruby places the newly defined methods(instance methods) inside it, and makes it the current class of the receiver of class_eval
    class_eval would declare instance methods, because class_eval can be called only on classes and modules in ruby - the name reflects the receiver
    instance_eval would declare class methods, because instance_eval can be called on every single object in ruby - the name reflects the receiver
    see file 'receiver_a_class__class_eval__creates_instance_methods.png' to find out what the 'def' method does inside class_eval
    see file 'receiver_a_class__instance_eval__creates_class_methods.png' to find out what the 'def' method does inside instance_eval
    see file 'receiver__xxx_eval__truth_table.png' to find out what the 'def' method does for all cases
  allows us to define methods or code inside a class given a class object
    should allow us to define instance methods on the class itself

Class Methods
  can be created with self
    should be called without an instance
  can be created with class << self when declaring more than one method at once
    should be called without an instance

Class Variables
  can be created with self
    should be called without an instance
  can be created with class << self
    should be called without an instance

enumeration
  using many different enumeration classes
    should define enumeration classes on the fly (ex: Color or ThreatLevel)
    should be distinct from other enumeration classes (ex: Color vs. ThreatLevel)
  using a single Enum class for all kind of enumerations
    should define Color or ThreatLevel enumeration on the fly without conflicting and remember its name/value

count with increment method
  hanis solution
    should return start first time called
    should return start + inc when called after first time(2nd)
    should return start + inc when called after first time(3rd)
    should return start + inc when called after first time(4th)
  daves solution
    should return start first time called
    should return start + inc when called after first time(2nd)
    should return start + inc when called after first time(3rd)
    should return start + inc when called after first time(4th)

define_method
  is available only inside a class or a module
    should allow us to create a simple statically named method inside a class
    should allow us to create a dynamically named method inside a class
    should allow us to create many dynamically named methods(i.e many similar methods) inside a class
    it should allow us to create a simple statically named method inside a module
      can access the method when using the 'include' keyword
      can access the method when using the 'extend' keyword
  is NOT available inside a singleton object, but just for classes or modules
    should raise error when we attempt to use it inside an object
  can be made available inside a singleton object with the help of class_eval
    should allow us to create methods inside a class
  can be made available inside a singleton object's ghost subclass with the help of class_eval
    should allow us to create methods inside a class

Enumerable filters
  - find
    returns the first item in the collection for which the block evaluates to true
    returns nil if no such item was found
  - detect (same as find)
    returns the first item in the collection for which the block evaluates to true
    returns nil if no such item was found
  - find_all
    returns all items in the collection for which the block evaluates to true
    returns empty array if no such item was found
  - select (same as find_all)
    returns all items in the collection for which the block evaluates to true
    returns empty array if no such item was found
  - reject
    returns all items in the collection for which the block evaluates to false (i.e removes items w/truthy blocks)
    returns empty array if none of the items for which the block evaluates to false
  - grep(x) or grep(/x/)
    returns all items in the collection for which x === item (i.e exact match)
    returns all items in the collection for which x ~= item (i.e item.match(/x/) - example 1)
    returns all items in the collection for which x ~= item (i.e item.match(/x/) - example 2)
    returns all items in the collection for which x ~= item (i.e item.match(/x/) - example 3)

Enumerable predicates
  - all?
    returns true if the given block evaluates to true for all items in the collection
    returns false if the given block evaluates to false for at least one item in the collection
  - any?
    returns true if the given block evaluates to true for any item in the collection (at least one)
    returns false if the given block evaluates to false for all items in the collection (none of the items evaluates to true)
  - include?(x)
    returns true if x is a member of the collection
    returns false if x is NOT a member of the collection
  - member?(x) (same as include)
    returns true if x is a member of the collection
    returns false if x is NOT a member of the collection

Enumerable transformers
  - map
    returns the transformed collection for which the block is applied to each element of original collection
  - collect (same as map)
    returns the transformed collection for which the block is applied to each element of original collection
  - partition (same as [select(&block), reject(&block)])
    returns 2 arrays for which all items in the collection whose blocks evaluate to true and false, respectively
  each_slice(n) - to split an array into slices containing n elements
    splits array into matching size with a default fill of nil
  - sort
    returns the transformed sorted collection by the given block or the elements own <=> operator
  - sort_by
    returns sorted collection using criteria in given block
  reduce - same as inject
    reduces an array with the help of some binary operation or block
  sort an array with reduce/inject - and in-order insert into new array
    sorts a literal array by reducing it and in-order inserting it into new array
    sorts a random array by reducing it and in-order inserting it into new array
  sort an array with looping and swapping - i.e a bubble sort
    sorts a literal array with a bubble sort
    sorts a random array with a bubble sort
  Factorials
    calculate a factorial
  use Set to remove duplicates from Array
    removes dups using Set
  use binary search to find element in Array
    finds an element in array using binary search

Including Enumerables in another data structure
  each should produce every element of the array
  Enumerator created from each without a block
  Enumerator allows chaining multiple enumerators
  when inlcuded in a class(Ex: LinkedList), Enumerable can augment its behavior
    traverse should return all elements the list when traversed
    and because LinkedList implements the each and <=> methods
      should be able to sort the LinkedList
      should be able to reverse sort the LinkedList
      should be able to get a count of the LinkedList
      should be able to use include? to see if an element is in the LinkedList
      should be able to use include? method
      should be able to use some aggregate methods from Enumerable
      should be able to chain methods from Enumerable
      should be able to reverse the list
      should be able to reverse with sort alone
      should be able to sort with help of <=>
      should be able to sum all element of the list
      should be able to exclude any element of the list
      should be able to find any element of the list
      should be able to find all elements of the list
      should be able to get first element of the list
      should be able to see if an element is a member? of the list

ruby hook methods
  - method related hooks:
    method_missing
      should do method_missing
    method_added
      should do be triggered everytime we add a method to a class
    singleton_method_added
      should do singleton_method_added
    method_removed
      should do method_removed
    singleton_method_removed
      should do singleton_method_removed
    method_undefined
      should do method_undefined
    singleton_method_undefined
      should do singleton_method_undefined
  - for Classes and Modules:
    inherited
      should show us who inherited from us with help of a stack
      should show us who inherited from us with help of a queue
    append_features
      should do append_features
    included
      should do included
    extend_object
      should do extend_object
    extended
      should do extended
    initialize_copy
      should do initialize_copy
    const_missing
      should do const_missing by brute force overriding original
      should do const_missing by using method alias chain and delegating to original when needed
      should be restricted to a single class or module when defined inside this class or module
  - for Marshalling:
    marshal_dump
      should do marshal_dump
    marshal_load
      should do marshal_load
  - for Coercion:
    coerce
      should do coerce
    induced_from
      should do induced_from
    to_s
      should do to_s
    to_sym
      should do to_sym
    to_proc
      should do to_proc
    to_string
      should do to_string
  - example of including comparable
    should trigger the space-ship operator (<=>) method
    should trigger the Ruby built-in hook method included

instance_eval
  allows us to execute any code inside a block - unlike eval
    should execte any code
    should execte any code with a receiver - unlike eval which is a private method
    inside the block of the receiver 'self' is set to the receiver of instance_eval
    allow us to cheat and access instance variables without a getter or a setter
      without instance_eval we should not be able to access the instance variable
      with instance_eval we should be able to access the instance variable (both read/write)
      without instance_eval we should not be able to access private methods
      with instance_eval we should be able to access private methods - i.e methods without a receiver
      instance_eval with a block produces a closure but not as we expect in that inside the block self is changed to the receiver of instance_eval
  allows us to define methods inside a block
    should allow us to define methods on any instance of a class
    everytime we call instance_eval on an object, ruby creates an anonymous ghost class and places the newly defined methods inside it, and makes it the class of the receiver of instance_eval
    should allow us to define methods on the class itself (class methods) - because a class is also an object in ruby
    everytime we call instance_eval on a class, ruby creates an anonymous ghost class and places the newly defined methods(class methods) inside it, and makes it the current class of the receiver(which is a class) of instance_eval
    class_eval would declare instance methods, because class_eval can be called only on classes and modules in ruby - the name reflects the receiver
    instance_eval would declare class methods, because instance_eval can be called on every single object in ruby - the name reflects the receiver
    see file 'receiver_a_class__class_eval__creates_instance_methods.png' to find out what the 'def' method does inside class_eval
    see file 'receiver_a_class__instance_eval__creates_class_methods.png' to find out what the 'def' method does inside instance_eval
    see file 'receiver__xxx_eval__truth_table.png' to find out what the 'def' method does for all cases
  allows us to create a Domain Specific Language(DSL) in a block
    should allow us to define and use a DSL inside a block - though people are moving away from such implementation because of wrong assumption that we have a closure around the block

instance variables
  are private to the object that created them
    should not allow access from the outside without a getter
    should allow access from the outside only when a getter is defined
    should not allow modification from the outside without a setter
    should allow modification from the outside only when a setter is defined with help of class_eval and attr_writer
    should allow modification from the outside only when a setter is defined with help of class_eval and attr_accessor

memoization examples - many ways to do it - subclassing, method rewriting, delegation
  example without memoization
    should execute expensive calculation each time
  memoization with a hash
    should execute expensive calculation once
  memoization using a subclass
    should execute expensive calculation once
  memoization using a subclass with code generation
    should execute expensive calculation once
  memoization using a subclass with code generation and without adding instance variables(@memory) to the original class - thanks to closure around define_method
    should execute expensive calculation once
  memoization using a ghost class to intercept the call chain in lieu of subclassing whole class
    should execute expensive calculation once
  memoization using a ghost class with code generation to create a hidden singleton class
    should execute expensive calculation once - see 'create_a_ghost_class_from_an_object.png'
  memoization using intrusive method re-writing after opening class
    should execute expensive calculation once
  memoization using a module to generically do the method re-writing
    should execute expensive calculation once
  memoization using a module and bind to generically do the method re-writing
    should execute expensive calculation once
  memoization using a module and a DSL to generically do the method re-writing
    should execute expensive calculation once
  memoization using Delegation
    should execute expensive calculation once

Modulization
  for namespacing constants and classes
    should provide access to the module's constant
    should provide access to the module's class
  for defining and finding module methods with namespaces
    should provide access to the module's methods
  for creating instance methods
    should provide access to the module's instance method
    should assert that to 'include' a module's instance method does not mean copying the method's body, but instead referencing the one and only copy of it
    should be available for use from more than one class
    should be available for use from an object with the 'extend' keyword
    should be available for use from an object by including the methods from the module into a singleton class using 'class << object'
  for converting a module instance methods into class methods of the class that is mixing in the module
    should provide access to the module's instance method as a class method using a singleton class using 'class << self'
    should provide access to the module's instance method as a class method using the 'extend' keyword
    should provide access to the module's instance method as a class method using the 'extend' keyword and from within the extending class at the class level
  for inheritance without subclassing
    with include and extend keywords
      must provide access to class methods and instance methods of the module
        should provide access to class methods
        should provide access to instance methods
    with include only keywords, and with help of self.included(the_class) ruby hook
      must provide access to class methods and instance methods of the module
        should provide access to class methods
        should provide access to instance methods
  using alias method chain to override a method in a class that includes a module
    without calling alias_method
      does not work as intended because any included modules are searched for methods after the class's own methods are searched, so you cannot directly overwrite a class's method by including a module
    with a call to alias_method
      works as intended in that they allow us to overwrite a class's method by including a module and using a different method name

my_attr_accessor
  provides an alternative implementation of attr_accessor with logging support
    should allow us to use it from any class
  a symbol is converted to string automatically when interpolated inside a string
    should produce a string class

my_attr_accessor_with_class_eval
  provides an alternative implementation of attr_accessor with class_eval instead of define_method because class_eval is more efficient in memory and speed
    should allow us to use it from any class

Object based inheritance or prototypal inheritance
  methods can be created from object instances
    methods can be called from the instance
    a cloned object inherits the instance methods
    a duplicated object does not inherit the instance methods
  cloned objects also inherit the state of the cloned object
    instance variables are cloned also

a Ruby method
  always return the last statement executed
    should return the last statememnt from if block when an if{...} statement is truthy
    should return the last statememnt from else block when an if{...} statement is falsy
    should return nil when we have 'if (nil)' as the last statement and no else block
    should return last statement in else block when we have if (nil) as the last statement

inheritance in practice using shipping example in one file
  using different shipping types
    should be able to Ship 16oz domestic
    should be able to Ship 90oz domestic
    should be able to Ship 16oz international

trace method calls
  - first iteration
    should trace a simple method everytime it is called with the help of define_method with block in Ruby 1.9
  - second iteration
    should trace a simple method everytime it is called with the help of class_eval and without define_method so that it can work with Ruby 1.8
  - third iteration
    should trace simple and complex methods(<<,[], and variable=) everytime it is called with the help of instance_method and Ruby 1.9
  - sidenote - prove that a CONST is available from within both a Ruby class and its instance
    should have access to the CONST at the class level
    should have access to the CONST from any instance of the class
  - forth iteration
    should trace simple and complex methods(<<,[], and variable=) everytime it is called with the help of instance_method and Ruby 1.8 and METHOD_HASH
  - fifth iteration
    should trace any existing class in Ruby 1.8 - try it with Time class
  - sixth iteration
    should trace any existing class in Ruby 1.8 - try it with String class - turn tracing OFF for String class while doing the tracing
  - seventh iteration
    should trace any existing class in Ruby 1.8 - with nested classes
  - eighth iteration
    should trace any existing class in Ruby 1.8 - with nested classes and dealing with recursive Array#inspect
  - nineth iteration
    should trace any existing class in Ruby 1.9 - with nested classes and with method_define with block support

inheritance in practice using shipping example in one file
  using different shipping types
    should be able to Ship 16oz domestic
    should be able to Ship 90oz domestic
    should be able to Ship 16oz international

Finished in 0.05986 seconds (files took 0.39194 seconds to load)
238 examples, 0 failures

About

my attempt at mastering ruby motivated by Dave Thomas video tutorials https://pragprog.com/screencasts/v-dtrubyom/the-ruby-object-model-and-metaprogramming

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published