tags : Programming Languages

Variable and scope

Variable

  • Local variables ([a-z] or _)
  • Global variable ($/$apple)
  • Instance variable (@/@apple)
  • Class variable (@@/@@apple)
  • Constant ([A-Z]/APPLE), Variables that start with a capital letter are constants.
  • nil and self are pseudo-variables

Class and instance variables are private , so getters and setters are necessary to access the variables outside the class. We can write our own getter and setters using @ and @@ but ruby provides some shortcuts for creation getter and setters of instance variables.

  • attr_accessor: creates the setter and getter methods.
  • attr_reader: create the getter method.
  • attr_writer: create the setter method.

If using rails, it extends Ruby with both mattr_accessor (Module accessor) and cattr_accessor (as well as _reader=/=_writer versions) to provide getter/setter methods at the class or module level.

There is another thing called class level instance variable which is not a class variable.

# class level instance variable "things"
class Parent
  @things = []
 
  class << self
    attr_accessor :things
  end
end
 
Parent.things #=> []
Parent.things << :car
Parent.things #=> [:car]

Inheritance with variables

  • Instance variables are not inherited.
  • Class is also an object in ruby. So a class can have instance variables. A class instance variable is not shared by the class’s descendants. (DOUBT: I think i mixed up some shit here.)
  • A class variable is shared among the class and all of its descendants.

And then classes have the initilize method which we can use to initilize stuff.

Scope

Scope refers to what variables are available at any given point in time.

OOP

class names tend to be nouns, whereas module names are often adjectives (Stack versus Stacklike).

class - ruby inheritance vs mixins - Stack Overflow

It seems like in ruby objects(internally) can’t have methods, only classes can. But in practice objects can have methods thaat are implemented by the singelton (*) superclass (sometimes called object’s metaclass or eigenclass). RObject, RClass are the internal representation in the MRI/YARV but not all objects(eg. String) fall into these two classes, they can have their own separate internal representation of what the class looks like.

# 3 kinds of ways we get to see methods in Ruby
class SayHello
  def self.from_the_class # class method
    "Hello, from a class method"
  end
 
  def from_an_instance
    "Hello, from an instance method"
    # note: name of a instance method and class method can be same!
  end
end
 
hello1 = SayHello.new
hello2 = SayHello.new
hello1.from_an_instance # instance method
 
# method on specific objects
def hello1.poop
    "cool"
end
hello1.poop
hello2.poop # error!
hello1.from_the_class #error!

How class methods are implemented, prural_name is a class method here

Modules

Defined as a collection of methods and constants. The superclass of Class is Module. When using modules, we use the scope resolution operator :: to access stuff inside it. The methods in a Module are only accessible if it is being included in a class. This including is sometime called mixin, so a module sometimes might be called in mixin.

Instead of using include we could also extend which does different stuff. If we use extend, ruby puts the methods from the module to the singleton class of that extending Class, i.e they get added as class methods. We can use extend on objects aswell, then they’ll be added to the singleton class of the object.

If multiple classes include that module, they’ll all point to the same thing.

module MyModule
  def self.some_method # a module method
    p "Hello, from a class method"
  end
end

How inclusion of Modules happen (instance methods). Module methods are are not accessible when included in Classes.

Aside about :: and .

  • The . operator basically says “send this message to the object”. When you write obj.meth, you’re sending the meth message to the object obj. obj will respond to meth if there is a method body defined for it.
  • The :: operator “drills down” to the scope defined to the left of the operator, and then calls the member defined on the right side of operator, it can help in accessing additional things such as constants in classes.
  • There is no difference between :: and . when calling class (static) methods in functionality.
  • ::ClassNameCool::ClassNameBool : absolute, gives access to top level from any context
  • ClassNameCool::ClassNameBool : relative

Metaprogramming Ideas

Contexts

There are three implicit contexts in Ruby: self, the scope used for constant lookup, and lastly the default definee.

Execution Context

The execution context contains the variables, the methods and self for a given scope.

More on this:

  • self

    This is the current object and the default receiver.

Evals

class_eval and instance_eval

A class name is simply a constant which points to an instance of the class Class.

  • These methods allow us to evaluate arbitrary code in the context of a particular class or object. The names are confusing doe. These make use of the singleton class and not actually modifying the specified class directly.
  • class_eval: creates instance method and belongs to the Module class, meaning that the receiver will be a module or a class.
  • instance_eval: creates class method and belongs to the Object class, meaning that the receiver will be an object. (But also works when reciever is a class (??))

Symbols

# ruby added some syntactic sugar to hash creation
# but it still creates hashes with symbols, eg. querying we need to use symbols
2.6.5 :032 > aab = {user: "Debanga", age: 23}
 => {:user=>"Debanga", :age=>23}
2.6.5 :033 > aab
 => {:user=>"Debanga", :age=>23}
2.6.5 :034 > aab[user]
Traceback (most recent call last):
        1: from (irb):34
NameError (undefined local variable or method `user' for main:Object)
Did you mean?  super
2.6.5 :035 > aab[:user]
 => "Debanga"
2.6.5 :036 >
# symbol-tp-proc
user = {"name" => "Hrihsikesh","age" => 23}
user.keys.map{|key| key.object_id}
# same as above
user.keys.map do |key|
  key.object_id
end
# the & converts a symbol to a proc object which can be passed to methods (??)
user.keys.map(&:object_id)

Read More:

Blocks, Procs, Lambdas and Closures

  • Blocks are essentially nameless functions. yield is used to call a block inside functions. We use &arg_name_here when we want to convert a block to a proc.
  • Procs essentially an block which is an object and can be passed around and used as a block. In ruby you can only pass a single block but you can pass multiple procs around in functions. (Proc.new can be used and <ProcObj>.call method can be used to call it explitcitly). > Proc.new and proc are alias syntax
  • lamdas These are like proc objects only, but has more things to offer.
    • One thing is picky about the arguments, you must pass the arguments to .call.
    • procs return from the context where they are defined.
    • lambdas return inside the function where .call was called from.
  • closures: What’s interesting about closures in ruby is that the value is not stored in the closure but the reference, so if we mutate the reference to a thing that is inside the closure, we can see updated value upon subsequent access to the closure.

Exception and errors

  • All Ruby exceptions and errors are an extension of the Exception class
  • In a nutshell, every custom Ruby exception should extend StandardError, rather than the Exception class(ctrl+c).

Misc

Boolean Methods

These are methods that end with a ?, The question mark is a valid character at the end of a method name. They are also called predicates, query. It’s just a convention and there are various examples in the ruby language, any?,=is_a?=,=tainted?=,=nil?= etc. But defined? does not return a bool but still uses ?

There is another place where ? is used to define a character literal (which is a discouraged style)

Destruction

By convention, if a method name ends with an exclamation mark, it does something destructive Eg. reverse!

class << foo block

Idk what this is, the << is the append operator

hash rockets

=>

spaceship operator

a <=> b

Ruby’s % Notation

The parenthesis can be almost any other character such as square brackets %w[...], curly braces %w{...} or even something like exclamation marks %w!...!. All of these have the same behavior (returning an array).

%q[ ] # Non-interpolated String (except for \\ \[ and \])
%Q[ ] # Interpolated String (default)
%r[ ] # Interpolated Regexp (flags can appear after the closing delimiter)
%i[ ] # Non-interpolated Array of symbols, separated by whitespace
%I[ ] # Interpolated Array of symbols, separated by whitespace
%w[ ] # Non-interpolated Array of words, separated by whitespace
%W[ ] # Interpolated Array of words, separated by whitespace
%x[ ] # Interpolated shell command

Other string notations.

Eager Loading in Active Records

There are like 3 ways, preload, eager_load and there’s a third one.

# without any preloading
posts = Post.published.limit(100)
# preloading
posts = Post.published.preload(:tags).limit(100)
# querying on the relationship that was preloaded
posts = Post.published.preload(:tags).where(tags:{name: "Rails"}).limit(100)
# eager_loading
posts = Post.published.eager_load(:tags).limit(100)
# querying on the relationship that was eager_loaded
posts = Post.published.eager_load(:tags).where(tags:{name: "Rails"}).limit(100)
# use include to automatically select one between eager_load or preload
posts = Post.published.includes(:tags).limit(100)
=begin
performance of preload vs eager_load is roughly similar,
but includes chooses eager_loading when querying on the loaded relationship.
=end

Kernel Module

The ||= thing

a ||= b # this behaves like a || a = b and NOT like a = a||b

Musings

  • In ruby you can have an Array as the Key of a Hash object.
  • I think nice way to think about modules and interfaces in Ruby
  • programmatically check if an object will respond to a method call (or any message for that matter) from whatever calls the method, using #respond_to?(:some_method)
  • This is possible because Rails implements an autoload system. It uses Module.const_missing to detect when you try to reference a constant that hasn’t been loaded. It then loads the files it believes should contain the constant. This works most of the time, but there’s a catch.
  • The creator of ruby worked for Heroku, hence so many things ruby is related to Heroku. And DHH we all know.
  • Plain Old Ruby Objects (POROs) LOLOL.
  • Rails autoloading --- how it works, and when it doesn’t
  • Note: Zeitwerk relies on the convention that each file will define the constant that is named after the name of the file (meaning that /comment.rb should define theComment constant). Luckily, this is not surprising for a normal Rails app. So Constants not only mean the constants we’re familiar with.
  • Reopening a class

Snippets

Ref

To Read