back to overview Looking for Rails?
- Basics
- Vars, Contants, & Symbols
- Operators
- Enumerables
- Regular Expressions (Regex)
- Blocks & Procs & Lambdas
- Methods
- Classes
- Eigenclass
- Singleton Methods
- Inheritance
- Patterns
- Modules
- Require/Load
- Commenting
- Conditions
- Printing & Putting
- String Methods
- User Input
- Loops
- Sorting & Comparing
- Exceptions
- Useful Methods
- Gems
- Tips
- Debugging
- Testing
- Links
irb
: to write ruby in the terminal- 'duck typing': Objects in Ruby are defined by the methods on them - "If it walks like a duck, and quacks like a duck, then it must be a duck"
'
strings are literal,"
strings allow interpolation and escapes- you can replace most
{}
withdo end
and vice versa –– not true for hashes or#{}
escapings - Best Practice: end names that produce booleans with ?, update the Object with !
- CRUD: create, read, update, delete
integer
: number without decimalfloat
: number with decimal- tag your variables:
-
$
: global variable
-
@
: instance variable
-
@@
: class variable
-
- starts with capital: constant
1_000_000
: 1000000 –– just easier to readnil
: null;
: statement separator - like newline::
: namespace resolution operator - for accessing other namespaces- Method calls are really messages:
# This 1 + 2 # Is the same as this ... 1.+(2) # Which is the same as this: 1.send "+", 2
PORO
== POJO
my_variable = “Hello”
my_variable.capitalize! # ! methods modify (and return) the object - same as my_variable = my_variable.capitalize
my_variable ||= "Hi" # ||= is a conditional assignment - only set the variable if it was not set before.
my_variable.object_id # unique refid for this object
MY_CONSTANT = "something" # starts with capital, unenforced, can't define in method
Class.constants # returns constants defined in class Class
const_get(:name) # return value of constant "name"
const_set(:name, value) # set value of constant "name" to value
const_missing(:symbol) # invoked when reference made to undefined constant, accepting constant name
const_defined?(:name) # returns true if constant is defined
remove_const(:name) # remove constant "name"
private_constant(:name) # makes constant private
# NOTE: Classes are constants!
module Food
class Bacon; end
end
Food.constants #=> [:Bacon]
Note: constant lookup starts with class containing the method called, NOT the class called on. See More
:symbol # use: internal identifiers in code (i.e. hash keys, reference method name)
# :symbol != "symbol" Immutable. Faster.
# Once used, any Symbol with the same name references the same memory Object
:test.to_s # converts to "test"
"test".to_sym # converts to :test
"test".intern # :test
==
,!=
,>
,<
,>=
,<=
as usual===
: used for case statements - exact comparison==
: compare values and not types<=>
: comparison, 0 if equal, 1 if a>b, -1 if a<b.eql?
: used for hash key comparisons, compare hash (value).equal?
: true if same object id (exact same object)
- Assignment operators:
+=
,-=
,*=
,/=
,%=
,**=
but NOT 1++ or 1-- a, b, c = 10, 20, 30
is the same asa = 10
b = 10
c = 10
- Normal:
+
,-
,*
,/
,%
,**
(exponent) - The shovel operator (<<)
"a" << "b" #=> "ab"
- note this actually modifies the object
condition ? "true" : "false"
: ternary1..9
: inclusive range (1..9)1...9
: exclusive range (1..8)defined? x
: true if x is (variable) initialized or (method) defined.
: "message" operator -> reference class variables/methods::
: "scope" operator -> reference class variables/methods and constants/namespaced things&.
: safe navigation, null coalesce&<object>
: if object is aProc
? convert to block : call<object>.to_proc
, then convert to block
my_array = [a,b,c,d,e]
my_array[1] # b
my_array[2..-1] # c , d , e
multi_d = [[0,1],[0,1]]
[1, 2, 3] << 4 # [1, 2, 3, 4] same as [1, 2, 3].push(4)
%w(foo bar) == ["foo", "bar"] # => true
hash = { "key1" => "value1", "key2" => "value2" } # same as objects in JavaScript
hash = { key1: "value1", key2: "value2" } # same hash, keys are turned into symbols
key1 = "value1", key2 = "value2"
hash = { key1:, key2: } # same hash, value fetched by name of key
Hash.new #=> {}
Hash([]) #=> {}
Hash(key: value) #=> {:key => :value}
h.default #=> nil - return on key not found lookups
h.default = 0 #
Hash.new("val") # default if key does not exist = "val"
h.default_proc #=> nil - if set, used instead of default
h.default_proc = proc { |hash, key| "#{key}" } # key not found lookups call proc
Hash.new{ |hash, key| "#{key}" } # default_proc set to block
h[:nokey] #=> "nokey" - note this doesn't add the key to hash
h.include?(:nokey) #=> false
pets["Stevie"] = "cat" # set key to value
pets["Stevie"] #=> cat
hash.select { |key, value| value > 3 } # selects all keys in hash that have a value greater than 3
hash.filter { |key, value| value > 3 } # alias to select
hash.each_key { |k| print k, " " } #=> key1 key2
hash.each_value { |v| print v, " " } #=> 1 2 3
hash.merge(other_hash) # merge together, other_hash overwrites duplicates
hash.reverse_merge(other_hash) # merge together, hash overwrites duplicates
{**{k1: 1, k2: 2}, **{k3: 3, k4: 4} } #=> {k1: 1, k2: 2, k3: 3, k4: 4} - ** merges/flattens hash
# technically can use anything that implements <=>
(-1..-5).to_a #=> []
(-5..-1).to_a #=> [-5, -4, -3, -2, -1]
('a'..'e').to_a #=> ["a", "b", "c", "d", "e"]
('a'...'e').to_a #=> ["a", "b", "c", "d"]
"bla,bla".split(“,”) #=> ["bla","bla"] takes string and returns an array
["1", "2"].each { |i| puts i } #=> 12 - do block on each element
enum.all?(&:valid?) # returns true/false if all elements return true for block
[1,2,3].collect {|i| i.to_str} #=> ["1", "2", "3"] runs block on every element and returns new array
[1,2,3].map(&:to_str) #=> ["1", "2", "3"] alias collect. NOTE: symbol to block with &
enum.reduce(symbol) # for each e in enum, memo.symbol(e) => memo
(5..10).reduce(:+) #=> 45
(5..10).reduce(1, :*) #=> 151200 - arg is initial value of memo, otherwise use first element
enum.inject {|memo, e| memo + e} # for each e in enum, run block producing memo
(5..10).inject{ |sum, n| sum + n } #=> 45 - note reduce == inject
(5..10).inject(1) { |p, n| p * n } #=> 151200 - initial value used
H = {"Key1" => 1, "Key2" => 2}
H.values.reduce(:+) #=> 3 - below are also 3, but note will return 0 if H is nil
H.values.reduce(0) { |sum,x| sum + x }
H.reduce(0) { |sum,(key,val)| sum + val }
object.freeze # now immutable, modifications will throw runtime error
enum.group_by { |x| y } # return hash: keys = y, values = array of x that correspond to y
(1..6).group_by { |i| i%3 } #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
OpenStruct.new # create new arbitrary object
aus = OpenStruct.new(:country => "Australia", :countryCode => "AUS")
p aus # => <OpenStruct country="Australia" countryCode=""AUS">
See more
ENV
object behaves like a hash and gives access to env vars defined outside ruby
ENV.keys # returns all env var keys
ENV["GEM_HOME"] #=> "/Users/josephcarino/.gem/ruby/3.1.2"
- Tester
- Wrap regex in '/'
text = "A regular expression pleases me"
text.match(/expression/) #=> MatchData or nil
text.scan(/\b[aeiou][a-z]*\b/) #=> ["expression"] - note case sensitive
Blocks are not objects. Enclosed in do..end or {}.
yield
: run block
def yield_name(name)
return "No block given" unless block_given?
yield("Kim") # print "My name is Kim. "
yield(name) # print "My name is <name>. "
end
yield_name("Eric") # => "No block given"
yield_name("Eric") { |n| print "My name is #{n}. " } # My name is Kim. My name is Eric.
yield_name("Peter") { |n| print "My name is #{n}. " } # My name is Kim. My name is Peter.
def explicit_block(&block)
block.call # same as yield
end
Function object. A proc is a saved block object.
cube = Proc.new { |x| x ** 3 }
[1, 2, 3].collect(&cube) #=> [1, 8, 27]
cube.call(3) #=> 27 call procs directly
def block(&the_block) # take block and turn it into a proc
the_block # return the proc
end
adder = block { |a, b| a + b } # adder is now a Proc object
adder.class #=> Proc
# any method that responds to #to_proc can be coerced to proc with & [see more](https://maximomussini.com/posts/ruby-to_proc)
class Doubler
def double(n)
n * 2
end
def to_proc
method(:double).to_proc
end
end
doubler = Doubler.new
[1, 2, 3].map(&doubler) #=> [2, 4, 6]
# Symbol.to_proc sends message of same name as symbol to object
[1,2,3].map(&:to_s) # => ["1", "2", "3"]
What is a lambda? #todo
lambda { |param| block }
multiply = ->(x) { x * 3 } #
multiply = lambda { |x| x * 3 } #same as prev line
multiply.call(4) # => 12
y = [1, 2].collect(&multiply) # 3 , 6
Diff between procs and lambdas:
- a lambda checks the number of arguments passed to it, while a proc does not (This means that a lambda will throw an error if you pass it the wrong number of arguments, whereas a proc will ignore unexpected arguments and assign nil to any that are missing.)
- when a lambda
return
s, it passes control back to the calling method; when a procreturn
s, it does so immediately, without going back to the calling method.
# Argument types are used in this order: required -> optional -> variable -> keyword
def testing(a, b = 1, *c, d: 1, **x) # required, optional, variable, keyword optional, double splat
p a,b,c,d,x
end
testing('a', 'b', 'c', 'd', 'e', d: 2, x1: 1, x2: 2)
=begin
"a"
"b"
["c", "d", "e"]
2
{:x1=>1, :x2=>2}
=> ["a", "b", ["c", "d", "e"], 2, {:x1=>1, :x2=>2}]
=end
def greet(hello, name, exclaim = false) # 2 args in order plus default value
return "#{hello}, #{name}#{exclaim ? '!' : ''}"
end
greet("Hi", "Joe") #=> "Hi, Joe"
def greet2(hello:, name:, exclaim: false) # 2 args by name plus default value
return "#{hello}, #{name}#{exclaim ? '!' : ''}"
end
greet2(hello:"Hi", name:"Joe") #=> "Hi, Joe"
def greet3(hello, *names) # *names is variable argument; other arguments combined into array
return "#{hello}, #{names}"
end
greet3("Hi", "Joe", "Justin") # => "Hi, [\"Joe\", \"Justin\"]"
object.method(:method_name) #=> like object.method_name, reflection
def greet4(opener, **exclaims) #=> create hash of named arguments that don't match
exclaims.reduce(opener) { |all, (key, val)| all + " #{key} says #{val}!"}
end
greet4(opener: "Greetings!", joe: "Hi", john: "Hello") #=> "Greetings! joe says Hi! john says Hello!"
custom objects
class Person # class names are PascalCase
@@count = 0
attr_reader :name # make it readable
attr_writer :name # make it writable
attr_accessor :name # makes it readable and writable
def Methodname(parameter)
@classVariable = parameter
@@count += 1
end
def self.show_classVariable # class method - static method, self = Person
@classVariable
end
def Person.get_counts # class method - static method
return @@count
end
private
def private_method; end # Private methods go here
def private_method_2; end # note this is still private (until end of block or next access modifier)
end
matz = Person.new("Yukihiro")
matz.show_name # Yukihiro
Everything has a metaclass called the eigenclass - copy of original class owned by instance
Eigenclass
: Class that follows the Singleton pattern
matz = Object.new
def matz.speak # define a method on the matz's object's metaclass
"Place your burden to machine's shoulders"
end
matz.class #=> Object - metaclass is invisible
class << matz # retarget self, add method to matz eigenclass
def speak2
'speak2'
end
end
metaclass = class << matz; self; end # assign metaclass to matz's class
metaclass.instance_methods.grep(/speak/) #=> ["speak", "speak2"] - defined on this instance, not on Object
Per-object methods, only available on the Object you defined it on
class Car
def inspect
"Cheap car"
end
end
porsche = Car.new
porsche.inspect # => Cheap car
def porsche.inspect
"Expensive car"
end
porsche.inspect # => Expensive car
# Other objects are not affected
other_car = Car.new
other_car.inspect # => Cheap car
class DerivedClass < BaseClass; end
class DerivedClass < Base
def some_method
super(optional args) # call super.some_method
# Some stuff
end
end
end
# Single inheritance only. Use mixins for multiple inheritance/extension
'Evaluating a member of a receiving object in the context of a sending object.'
# SimpleDelegator
require 'delegate'
User = Struct.new(:first_name, :last_name)
class UserDecorator < SimpleDelegator
def full_name
"#{first_name} #{last_name}"
end
def first_name
"#{super[0]}." # call corresponding method on wrapped object
end
end
john_user = User.new("John", "Doe")
decorated_user = UserDecorator.new(john_user) #=> create delegator wrapping User object
decorated_user.first_name #=> "J."
decorated_user.full_name #=> "J. Doe"
# DelegateClass(ClassToDelegateTo)
class Logfile < DelegateClass(File)
MODE = File::WRONLY|File::CREAT|File::APPEND
def initialize(basename, logdir = '/var/log')
path = File.join(logdir, basename) # Create logfile in logdir location
logfile = File.open(path, MODE, 0644)
super(logfile) # call Delegator.initialize. Logfile now wraps File
end
end
# Forwardable
require 'forwardable'
User = Struct.new(:first_name, :last_name)
class UserDecorator
extend Forwardable
def_delegators :@user, :first_name # explicitly declare for each method to forward
def_delegator :@user, :last_name, :family_name # explicitly declare last_name and rename to family_name
def initialize(user)
@user = user
end
def full_name
"#{first_name} #{family_name}"
end
end
john_user = User.new("John", "Doe")
decorated_user = UserDecorator.new(john_user)
decorated_user.full_name #=> "John Doe"
decorated_user.last_name #=> NoMethodError
# delegate
User = Struct.new(:first_name, :last_name)
class UserDecorator
attr_reader :user
delegate :first_name, to: :user
delegate :last_name, to: :user, prefix: true # alternate: prefix: account to add account_last_name
# also: :allow_nil (return null on nil user)
def initialize(user)
@user = user
end
def full_name
"#{first_name} #{user_last_name}"
end
end
decorated_user = UserDecorator.new(User.new("John", "Doe"))
decorated_user.full_name #=> "John Doe"
# casting
require 'casting'
User = Struct.new(:first_name, :last_name)
module UserDecorator
def full_name
"#{first_name} #{last_name}"
end
end
user = User.new("John", "Doe")
user.extend(Casting::Client) # alternate: include Casting::Client inside User class
user.delegate(:full_name, UserDecorator)
Object (subject) maintains list of dependents (observers), and automatically notifies them of any state changes
require "observer"
class Moderator
include Observable
def initialize(name)
@name = name
end
def write
message = "Computer says: No"
changed # assert subject has changed
notify_observers(message) # notify observers, call their :update method
end
end
class Subscriber
def initialize(moderator, limit)
@limit = limit
moderator.add_observer(self) # add to subject's observers list
end
def update(message) # required to be observer
puts "#{message}"
end
end
moderator = Moderator.new("Rupert")
Subscriber.new(moderator, 1)
moderator.write
moderator.write
# prints "Computer says: No" twice
require 'singleton'
class Logger
include Singleton
def initialize
puts 'Created'
end
end
Logger.new #=> NoMethodError - constructor is private
first = Logger.instance # prints 'Created'
second = Logger.instance # prints nothing
first == second #=> true
module ModuleName # module names are rather written in PascalCase
# variables in modules doesn't make much sence since modules do not change. Use constants.
end
Math::PI # using PI constant from Math module. Double colon = scope resolution operator
require 'date' # to use external modules.
puts Date.today # 2016-03-18
module Action
def jump
@distance = rand(4) + 2
puts "I jumped forward #{@distance} feet!"
end
end
# If you *include* a class with a module, you're "bringing in" the module's methods as instance methods. See Mixin
# If you *extend* a class with a module, you're "bringing in" the module's methods as class methods.
module A
def say
puts "this is module A"
end
end
class B
include A
end
class C
extend A
end
B.say #=> undefined method 'say' for B:Class
B.new.say #=> this is module A
C.say #=> this is module A
C.new.say #=> undefined method 'say' for C:Class
# Note: include/extend methods have scope of original module, not class called from
Similar to import/using. Returns true if found.
load `example_module.rb` # Load file into code when code executed
require './app/example_file.rb' # Load file into code when code executed, caches file
require 'example_file' # shortened name - depends on the listed directories in $LOAD_PATH
require_relative 'example_file.rb' # use for files in your directory
require_dependency
=begin
Bla
Multyline comment
=end
# single line comment
Note everything except nil
and false
are true (including 0
).
if 1 < 2
puts “one smaller than two”
elsif 1 > 2 # *careful not to mistake with else if. In ruby you write elsif*
puts “elsif”
else
puts “false”
end
# or
puts "be printed" if true
puts 3 > 4 ? "if true" : "else" # else will be putted
unless false # unless checks if the statement is false (opposite to if).
puts “I’m here”
else
puts “not here”
end
# or
puts "not printed" unless true
case my_var
when "some value"
###
when "some other value"
###
else
###
end
# or
case my_var
when "some value" then ###
when "some other value" then ###
else ###
end
&&
: and||
: or!
: not(x && (y || w)) && z
: use parenthesis to combine arguments- problem = false
- print "Good to go!" unless problem –– prints out because problem != true
print "bla"
puts "test" # puts the text in a separate line
"Hello".length # 5
"Hello".reverse # “olleH”
"Hello".upcase # “HELLO”
"Hello".downcase # “hello”
"hello".capitalize # “Hello”
"Hello".include? "i" # equals to false because there is no i in Hello
"Hello".gsub!(/e/, "o") # Hollo
"1".to_i # transform string to integer –– 1
"test".to_sym # converts to :test
"test".intern # :test
:test.to_s # converts to "test"
gets # is the Ruby equivalent to prompt in javascript (method that gets input from the user)
gets.chomp # removes extra line created after gets (usually used like this)
i = 1
while i < 11
puts i
i = i + 1
end
i = 0
until i == 6
puts i
i += 1
end
for i in 1...10 # ... tells ruby to exclude the last number (here 10 if we .. only then it includes the last num)
puts i
end
i = 0
loop do
i += 1
print "I'm currently number #{i}” # a way to have ruby code in a string
break if i > 5
end
for i in 1..5
next if i % 2 == 0 # If the remainder of i / 2 is zero, we go to the next iteration of the loop.
print i
end
things.each do |item| # foreach(item in things)
print “#{item}"
end
on hashes like so:
hashes.each do |x,y| # foreach(x,y in hashes)
print "#{x}: #{y}"
end
10.times do
print “this text will appear 10 times”
end
10.upto(15) { |x| print x, " " } # 10 11 12 13 14 15
"a".upto("c") { |x| print x, " " } # a b c
array = [5,4,1,3,2]
array.sort! # = [1,2,3,4,5] – works with text and other as well.
"b" <=> "a" # = 1 – combined comparison operator. Returns 0 if first = second, 1 if first > second, -1 if first < second
array.sort! { |a, b| b <=> a } # to sort from Z to A instead of A to Z
- Exception > StandardError > errors. Do not rescue Exception, used for system level events/signals.
- If raised but not yet handled,
$! contains exception and $ @ contains its backtrace - Put custom errors into module, inherit from a base error (StandardError > MyLib::Error > MyLib::CustomError)
begin
raise ArgumentError.new("arg")
raise RuntimeError, "You messed up!"
raise "You messed up!" # raises RuntimeError if unspecified
rescue ArgumentError, RuntimeError => error
#... error handler for ArgumentError or RuntimeError
retry # optional - retry begin block
rescue => error # StandardError
#... error handler for any other errors
else
#... executes when no error
ensure
#... always executed - 'finally'
end
grade = 100 / nil rescue 0.0 # => 0.0
for x in expression do
#... loop body
end rescue ...error handler... # note loop will exit when rescued. use next in handler to coninue
.each {} rescue ...error handler...
1.is_a? Integer # returns true
:1.is_a? Symbol # returns true
"1".is_a? String # returns true
1.2.floor #=> 1 - rounds a float (a number with a decimal) down to the nearest integer.
cube.call # where 'cube' is a proc, calls procs directly
Time.now # displays the actual time
External packages
$ gem search ^rails
$ gem install drip
$ gem list
Require a gem places its /lib directory on the load path
require 'ap' #=> true
pp $LOAD_PATH.first # ".../gems/awesome_print-1.0.2/lib"
Everything has a value
x = 10
y = 11
z = if x < y
true
else
false
end
z # => true
Everything is an object
MyClass = Class.new do
attr_accessor :instance_var
end
# This is the same as
# class MyClass
# attr_accessor :instance_var
# end
self
- basically 'this' but changes in context
module Reflection
def reflect
self
end
end
class Ghost
include Reflection #or 'extend Reflection', same outcome - Ghost.reflect == Ghost is true
end
g = Ghost.new
g.reflect == g # => true
And you can retarget self:
class << "test"
puts self.inspect
end
# => #<Class:#<String:0x007f8de283bd88> - this is the metaclass
You can use the above to easily create class methods:
class Person1
def self.species
"Homo Sapien"
end
self.name #=> "Person1"
end
Person1.name #=> "Person1"
Person1.species #=> "Homo Sapien"
class Person2
class << self
def species
"Homo Sapien"
end
self.name #=> ""
end
end
Person2.name #=> "Person2"
Person2.species #=> "Homo Sapien"
class << Person3
def species
"Homo Sapien"
end
self.name #=> ""
end
Person3.name #=> "Person3"
Person3.species #=> "Homo Sapien"
# with instance_eval, new methods are defined on the metaclass, but the self is the object itself
Person4.instance_eval do
def species
"Homo Sapien"
end
self.name #=> "Person4"
end
Person4.name #=> "Person3"
Person4.species #=> "Homo Sapien"
At Shopify For Spin For Spin on SFN
someObject.inspect #=> lots of info about this object
someObject.class.name #=> someObject's class' name
someObject.instance_of?(someObject.class) #=> true
someObject.methods.inspect #=> list of methods you can call on someObject
someObject.respond_to?(:someMethod) #=> true if someObject has method someMethod
someObject.instance_variables.inspect #=> [:@instanceVar, :@instanceVar2]
ObjectSpace.each_object.inject(Hash.new 0) { |h,o| h[o.class] += 1; h }.sort_by { |k,v| -v }.take(10).each { |klass, count| puts "#{count.to_s.ljust(10)} #{klass}" }
# Prints table with object count for top-10 classes
ObjectSpace.each_object(String).sort_by { |s| s.size }.each { |s| p s }
# Prints all in-memory strings, sorted by size
o = "a" * 100
ObjectSpace.memsize_of(o) # prints size in memory of object
- Note: 'Enter' repeats last command
byebug
- add to source to break into debugger at next lineq[uit]
- quit and exit program (useq!
for no prompt) orkill
to REALLY quitrestart
- restart the program and byebugh[elp] [<command-name>]
- get help on<command-name>
or list all commandsb[ack]t[race]
orw[here]
- display stack tracehist[ory] <n>
- display last<n>
commands (default all)
c[ontinue] [<line-number>]
- continue until end, breakpoint, or line<line-number>
n[ext] [<x>]
- continue for<x>
lines (default 1)s[tep] [<x>]
- step into, repeat<x>
times (default 1)fin[ish] [<num-frames>]
- run until<num-frames>
have returned (default current)
b[reak] [<filename>:<line-number>|<class>(.|#)<method>] [<condition>]
- set breakpoint at<filename>:<line-number>
or start of<method>
in<class>
or default current line - if optional<condition>
is trueinfo breakpoints
- list all breakpointscond[ition] <n> [<expression>]
- add condition<expression>
to breakpoint<n>
or removes if blankdel[ete] [<n>]
- deletes breakpoint<n>
or all breakpointsdisable breakpoints [<n>]
- disable breakpoint<n>
or all breakpointscat[ch] <exception> [off]
- enable or disable (withoff
) catchpoint on<exception>
cat[ch]
- list all catchpointscat[ch] off
- deletes all catchpointssk[ip]
- passes caught exception back to application
f[rame] [<n>]
- moves to frame number<n>
or show current frame (default)up [<n>]
- move up<n>
frames (default 1)down [<n>]
- move down<n>
frames (default 1)
info [args|locals|instance_variables|global_variables|variables]
- show info of current framem[ethod] <class|module>
- show instance methods of given<class>
/<module>
m[ethod] i[nstance] <object>
- show methods of<object>
m[ethod] iv <object>
- show instance variables of<object>
- same asv[ar] i[nstance] <object>
v[ar] cl[ass]
- show class variables of selfv[ar] co[nst] <object>
- show constants of<object>
v[ar] g[lobal]
- same asinfo global_variables
v[ar] l[ocal]
- same asinfo locals
th[read]
- show current threadth[read] l[ist]
- list all threadsth[read] <number>
- switch context to thread<number>
th[read] stop|resume <number>
- stop or resume thread<number>
e[val]|p <exp>
- evaluate<exp>
and print (you don't neede|p
butset noautoeval
disables this)pp <exp>
- evaluate<exp>
and pretty-printputl <exp>
- evaluate<exp>
with an array result and columnize the outputps <exp>
- evaluate<exp>
with an array result, sort and columnize outputdisp[lay] <exp>
- evaluate<exp>
whenever program haltsinfo display
- list current display expressions - same asdisp[lay]
no argsundisp[lay] [<n>]
- remove display expression<n>
or alldisable|enable display <n>
- disable/enable display expression<n>
Options: autoeval, autoirb, autolist, autoreload, autosave, basename, callstyle, forcestep, fullpath, histfile, histsize, linetrace, tracing_plus, listsize, post_mortem, stack_on_error, testing, verbose, width
save <file>
- save current session options in file<file>
source <file>
- load current session options from file<file>
set <option>
- change value of option<option>
show <option>
- view value of option<option>
reload
- reload source codeinfo file
- information about the current source fileinfo files
- all currently loaded filesinfo line
- shows the current line number and filenamel[ist]
- shows source code after the current linel[ist] –
- shows source code before the current linel[ist] =
- shows source code centred around the current linel[ist] <first>-<last>
- shows all source code from<first>
to<last>
line numbersedit [<file:lineno>]
- edit<file>
(default current file)
require 'rspec/autorun'
describe Factorial do
let(:calculator) { Factorial.new } # available to all it in this describe, lazy
#let! is eager
it "finds the factorial of 5" do # test name
expect(calculator.factorial_of(5)).to eq(120)
end
end
describe Factorial do
#subject is special let, and auto-defined to the describe
subject { Factorial.new } # this is the default implicit subject and not necessary
subject(:calculator) { Factorial.new } # same as let, but allows implicit and named use of subject
it { should be_an_instance_of(Factorial) }
it { calculator.should be_an_instance_of(Factorial) }
end
class Factorial
def factorial_of(n)
(1..n).inject(:*)
end
end
You should use expect instead of should/should_not except in one liners
expect(x).to be_y # where y is a predicate method (i.e. empty? nil?) to be called on x
expect([1,2,3]).to include(3)
expect([1,2,3]).to start_with(1)
expect([1,2,3]).to end_with(3)
expect(/like/).to match('do you like') # regex match
expect(3).to be_between(1,3)
expect(2).to be_between(1,3).exclusive # default inclusive
expect({:a => 1, :b => 2}).to have_key(:a)
expect({:a => 1, :b => 2}).to have_value(1)
expect{ :x.count }.to raise_error(NoMethodError) # use block
expect{ stock.increment }.to change(stock, :value).by(100)
# double = mock object
obj = double("identifier")
obj = instance_double("class_name") # mock of class_name
obj = double("book", :title => "The RSpec Book") # creates test double and declare method stub
# specify a return value using `:expect` syntax
allow(obj).to receive(:message) { :value }
allow(obj).to receive(:message).and_return(:value)
allow(obj).to receive(:message).and_return(1, 2, 3) # return values in order, last one for subsequent
# raise/throw
allow(obj).to receive(:message).and_raise("this error")
allow(obj).to receive(:message).and_throw(:this_symbol)
allow(obj).to receive(:message) { raise "this error" }
allow(obj).to receive(:message) { throw :this_symbol }
# arguments
allow(obj).to receive(:message).with('an argument') { ... }
allow(obj).to receive(:message).with('more_than', 'one_argument') { ... }
allow(obj).to receive(:message).with(anything()) { ... }
allow(obj).to receive(:message).with(an_instance_of(Money)) { ... }
allow(obj).to receive(:message).with(hash_including(:a => 'b')) { ... }
allow(obj).to receive(:message).with(array_including(1,2,3)) { ... }
allow(obj).to receive(:message).with(/abc/) { ... }
# chaining
allow(double).to receive_message_chain("foo.bar") { :baz }
allow(double).to receive_message_chain(:foo, :bar => :baz)
allow(double).to receive_message_chain(:foo, :bar) { :baz }
# Given any of the above forms:
double.foo.bar # => :baz
#replace 'allow' with 'expect' to validate message received
# verify object received message
invite = spy("invite") # => same as `double("invite").as_null_object` (or use instance_double etc)
user.accept_invitation(invite)
expect(invite).to have_received(:accept)
# You can also use other common message expectations. For example:
expect(invite).to have_received(:accept).with(mailer)
expect(invite).to have_received(:accept).twice
expect(invite).to_not have_received(:accept).with(mailer)
# One can specify a return value on the spy the same way one would a double.
invite = spy('invite', :accept => true)
expect(invite).to have_received(:accept).with(mailer)
expect(invite.accept).to eq(true)
# before/after all or each test
describe Shop do
before(:all) { Shop.prepare_database }
before(:each) {Shop.startup}
after (:each) {Shop.teardown}
after (:all) { Shop.cleanup_database }
end
# group related tests with context blocks
describe Course do
context "when user is logged in" do
it "displays the course lessons" do
end
it "displays the course description" do
endFac
end
context "when user it NOT logged in" do
it "redirects to login page" do
end
it "it shows a message" do
end
end
end
# don't use shared_examples
# Use let (lazy) or let! (eager, before each example) to define a memozied helper method
$count = 0
describe "let" do
let(:count) { $count += 1 }
it "memoizes the value" do
count.should eq(1)
count.should eq(1)
end
it "is not cached across examples" do
count.should eq(2)
end
end
$count = 0
describe "let!" do
invocation_order = []
let!(:count) do
invocation_order << :let!
$count += 1
end
it "calls the helper method in a before hook" do
invocation_order << :example
invocation_order.should == [:let!, :example]
count.should eq(1)
end
end
# NOTE: skip tests with xit instead of it
- The
create
method persists the model instance (creates a DB record) while thebuild
method does not
ruby tests.rb [-e name_of_test]
- run tests including 'name_of_test', or all
-f [p|d|j|html]
- change output format, default isp[rogress]
: ./F/E--profile
- output time for each test