This is a very open ended question and you might be better of to start with the basics first:
> "string".object_id => 5263280 > "string".object_id => 5220980 > "string".object_id => 5179720 > :symbol.object_id => 329128 > :symbol.object_id => 329128 > :symbol.object_id => 329128
Notice that the object_id stays the same when symbols are created. This happens because the ruby interpreter uses the same heap memory location each time. The symbol was never completely released. In the case of strings the memory is marked for cleanup each time and a new memory is allocated.
@@
prefix,class DemoClass @@my_var = nil def initialize @@my_var = "hello world" end def my_var puts @@my_var end end class Demo2Class < DemoClass def initialize @@my_var = "goodbye world" end end demo1 = DemoClass.new demo1.my_var # => hello world demo2 = Demo2Class.new demo2.my_var # => goodbye world demo1.my_var # => goodbye world
@
symbol,$
symbol,trace_var :$foo, proc{puts "$foo is now #{$foo}"} $foo = 7 # => $foo is now 7
initialize
which is called automatically when object is instanciated: class Picture def initialize puts "In a constructor" end end Picture.new # => In a constructor
Module#define_method(name, body)
, where name
is a method's name and body
is a Proc
, Method
, UnboundMethod
or a block literal, allowing methods to be defined at runtime, class Conjure def self.conjure(name, body) define_method(name, body) end end Conjure.conjure(:glark, ->{ (3..5).to_a * 2}) Conjure.new.glark #=> [3, 4, 5, 3, 4, 5]
Module#define_method
is a private method so it must be called from within the class the method is being defined on,class_eval
: Array.class_eval do define_method(:second, ->{self.[](1)}) end [3, 4, 5].second #=> 4
Kernel#define_singleton_method
is called to define a singleton method on the receiver: Array.define_singleton_method(:second) do |array| array[1] end Array.second([2, 3, 4]) #=> 3
Range
is used to declare continuous variables: numerical_range = (1..4).to_a #=> [1, 2, 3, 4] character_range = ('bar'..'bat').to_a #=> ["bar", "bas", "bat"]
Array
.class MyClass def initialize(*args) if args.size < 2 || args.size > 3 puts 'This method takes either 2 or 3 arguments' else if args.size == 2 puts 'Found two arguments' else puts 'Found three arguments' end end end end MyClass.new([10, 23], 4, 10) #=> Found three arguments MyClass.new([10, 23], [14, 13]) #=> Found two arguments
&&
has higher precedence than and
: foo = 3 bar = nil a = foo and bar # => nil a # => 3 a = foo && bar # => nil a # => nil
Acts like (a = foo) and bar
, and
has lower precendence than =
,
&
operator works differently depending on the context:14 & 13 => 12 # 1110 & 1101 = 1100 (in binary)
: http://ablogaboutcode.com/2012/01/04/the-ampersand-operator-in-ruby/
Method | Effect |
---|---|
attr_reader :v | def v; @v; end |
attr_writer :v | def v=(value); @v=value; end |
attr_accessor :v | attr_reader :v; attr_writer :v |
!
indicates that the method is destructive in some way (for example it changes the object itself)Module
is like a class, but it can't be instantiated or subclassed,
module Logger def log(msg) puts "Message: #{msg} from #{self.to_s}" end end class Program include Logger end ph = Program.new ph.log("Hello!") #=> Message: Hello! from #<Program:0x00000001d11e28>
Singleton
: require 'singleton' class Logger include Singleton def log(msg) puts(msg) end end Logger.instance.log("Test message") #=> Test message Logger.new.log("Will it work?") #=> singleton.rb:12:in `<main>': private method `new' called for Logger:Class (NoMethodError)
require 'observer' # Periodically fetch stock price class Ticker include Observable attr_accessor :price def initialize(symbol, price) @symbol = symbol @price = price end def run last_price = nil loop do @price = @price + Random.rand(11) puts "Current price: #{price}" unless @price == last_price # Notify observers changed last_price = @price notify_observers(Time.now, @price) end end end end # All Warners are observers class Warner def initialize(ticker) ticker.add_observer(self) end end class SMSAlert < Warner def update(time, price) puts "--- #{time.to_s}: SMS Alert for price: #{price}" end end class EmailAlert < Warner # Callback for observer def update(time, price) puts "+++ #{time.to_s}: Email Alert Price changed to #{price}" end end ticker = Ticker.new("MSFT", 307) SMSAlert.new(ticker) EmailAlert.new(ticker) ticker.run #=> Current price: 317 #=> --- 2013-06-11 19:28:21 +0200: SMS Alert for price: 317 #=> +++ 2013-06-11 19:28:21 +0200: Email Alert Price changed to 317 #=> Current price: 323 #=> --- 2013-06-11 19:28:21 +0200: SMS Alert for price: 323 #=> +++ 2013-06-11 19:28:21 +0200: Email Alert Price changed to 323 #=> Current price: 332 #=> --- 2013-06-11 19:28:21 +0200: SMS Alert for price: 332 #=> +++ 2013-06-11 19:28:21 +0200: Email Alert Price changed to 332
map
goes through each element in a collection and replaces it with the value returned from a block of code,reduce
accumulates a value across the members of a collection,.map(&xxx)
syntax, you're telling Ruby to pass the Proc
object held in xxx
to map
method as a block.If xxx
is not a Proc
object, Ruby tries to coearce it into one by sending it a to_proc
message. If a symbol is passed, to_proc
is called on the symbol. This method would look like so if it was implemented in Ruby: def to_proc proc {|obj, *args| obj.send(self, *args) } end
require()
speed improvements,def foo(foo: 'bar', baz: 'qux', **rest) # Do something end foo(baz: 'qux', foo: 'frob')
module IncludableModule def something; end end class MyClass prepend IncludableModule end
to_infinity = (0..Float::Infinity) beyond = to_infinity.lazy.select do |n| num % 42 == 0 end 100.times do {|n| puts beyond.next }
set_trace_func
: trace = TracePoint.new(:raise) do |t| puts "Alert: Exception raised!" end trace.enable
module MyString refine String do def palindrome? self == self.reverse end end end using MyString # Monkey patch now active for context
%i{eeny meeny miny moe}
,(1..9999999).bsearch(12345)
DIR
, shows absolute path to file's directory,.to_h
, useful for converting Structs
.MAREK = "Marek".freeze def marek?(name) name == MAREK end
or, in Ruby 2.1:
def marek?(name) name == "marek"f end
def foo(a: 10) puts a end foo(a: 20) # => 20 foo # => 10
def foo(a:) puts a end foo(a: 20) # => 20 foo # => ArgumentError: missing keyword: a
# Ruby 2.0 def foo() end # => nil # Ruby 2.1 def foo() end # => :foo
Useful for metaprogramming.
"abc\u3042\x81".scrub("*") #=> "abc\u3042*"
Procs
and lambdas
are all closures. This means that they hold the values of any variables that were around when they were created.{…}
) or do…end
syntax,yield
defers the execution of the calling method in order to evaluate the block; the result of the block, if any, is then evaluated by any remaining code in the method; yield
accepts parameters, which are then passed into and evaluated within the block,yield
will execute the code within the block provided to the method,Procs
(in an Array
for example),Procs
, but lambda
checks the number of arguments it receives and returns ArgumentError
if they don't match,→
can also be used to create lambdas
,Procs
stop the method execution: def lambda_return lambda {return "I'm a lambda!"}.call return "Still running after lambda!" end def proc_return Proc.new {return "I'm just a Proc. :-("}.call return "Still running after proc!" end puts lambda_return puts proc_return
lambdas
conceptually work like methods, they enforce correct number of arguments and do not override the calling methods return, Procs
are more like drop-in code snippets,return
keyword in them, so a Proc
with return
passed into a method would give a LocalJumpError
, but a lambda
acts like a method, and this in turn can have return
in it: def generic_return(code) one, two = 1, 2 three, four = code.call(one, two) return "Give me a #{three} and a #{four}" end puts generic_return(lambda { |x, y| return x + 2, y + 2 }) puts generic_return(Proc.new { |x, y| return x + 2, y + 2 }) puts generic_return(Proc.new { |x, y| x + 2; y + 2 }) puts generic_return(Proc.new { |x, y| [x + 2, y + 2] }) # => Give me a 3 and a 4 # => unexpected return (LocalJumpError) # => Give me a 4 and a # => Give me a 3 and a 4
method
keyword: class Array def iterate!(code) self.each_with_index do |n, i| self[i] = code.call(n) end end end def square(n) n ** 2 end array = [1, 2, 3, 4] array.iterate!(method(:square)) puts array.inspect # => [1, 4, 9, 16]
INFO:
pop
and push
, Array
can be treated like a stack,push
and shift
, Array
can be used like a first-in first-out (FIFO) queue,Integer
Hash
means storing two things: index (the key) and the value,Object#eql?
and Object#hash
since Set
uses Hash
as storage,Useful when:
INFO: http://spin.atomicobject.com/2012/09/04/when-is-a-set-better-than-an-array-in-ruby/
Struct
and OpenStruct
imply more concrete relationship amongst the data than Hash
, but they don't have the instance methods as would Class
,OpenStruct
, take less memory,Use case:
INFO:
==
, a generic “equality”, at Object
level, it only returns true
if compared objects are the same object. This method is typically overridden in descendant classes to provide class-specific meaning,===
, a case equality, at Object
level it effectively is the same as calling #==
, it is typically overriden to provide meaningful semantics in case
statements,eql?
- generic equality, returns true
if compared objects have the same value. Used by Hash
to test members for equality. For objects of class Object
, eql?
is synonymous with ==
,equal?
- identity comparison, this should never be overriden, it is used to determine object identity (a.equal?(b)
iff a
is the same object as b
).More info:
rake db:rollback rake db:rollback STEP=1 # Rollback to the beginning rake db:reset # or if you're not worried about data rake db:purge
class ProductSweeper < ActionController::Caching::Sweeper observe Product# This sweeper is going to keep an eye on the Product model # If our sweeper detects that a Product was created call this def after_create(product) expire_cache_for(product) end # If our sweeper detects that a Product was updated call this def after_update(product) expire_cache_for(product) end # If our sweeper detects that a Product was deleted call this def after_destroy(product) expire_cache_for(product) end private def expire_cache_for(product) # Expire the index page now that we added a new product expire_page(:controller => 'products', :action => 'index') # Expire a fragment expire_fragment('all_available_products') end end
<% Order.find_recent.each do |o| %> <%= o.buyer.name %> bought <%= o.product.name %> <% end %> <% cache do %> All available products: <% Product.all.each do |p| %> <%= link_to p.name, product_url(p) %> <% end %> <% end %>
class ProductsController < ActionController caches_page:index def index @products = Products.all end end
REST is a set of principles that define how Web standards, such as HTTP and URIs, are supposed to be used (which often differs quite a bit from what many people actually do). The promise is that if you adhere to REST principles while designing your application, you will end up with a system that exploits the Web’s architecture to your benefit. In summary, the key principles are:
type
,class Company < ActiveRecord::Base; end class Firm < Company; end class Client < Company; end class PriorityClient < Client; end
When you do Firm.create(name: “sqTech”)
, this record will be saved in the companies table with type Firm
,
Yes Rails gives you complete freedom to use all traditional means of scaling an application. Things like memcached, caching full pages, caching fragments are all supported.
You can use any standard CDN to serve your media and static content as well.
Database scaling using sharding is supported.
Finally heroku makes your life easier by giving you the flexibility to scale up/down based on your need. Mostly websites have a peak time during which you need more servers and then there is a sleep time. Heroku makes that on-demand scaling process simpler. Companies such as HireFireApp.com makes the autoscale process easier.
Use:
User.where("login = ? AND password = ?", entered_user_name, entered_password).first # or User.where(:login => entered_user_name, :password => entered_password).first
instead of:
User.first("login = '#{params[:name]}' AND password = '#{params[:password]}'")
because of:
SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1
belongs_to
has_one
has_many
has_many :through
has_one :through
has_and_belongs_to_many
customer.orders # retrieves orders from the database customer.orders.size # uses the cached copy of orders customer.orders.empty? # uses the cached copy of orders
true
to the assocition call will reload the cache: customer.orders # retrieves orders from the database customer.orders.size # uses the cached copy of orders customer.orders(true).empty? # discards the cached copy of orders # and goes back to the database
class Customer < ActiveRecord::Base has_many :orders end class Order < ActiveRecord::Base belongs_to :customer end
This happens because c
and o.customer
are two different in-memory representations of the same data, and neither one is automatically refreshed from changes to the other. Active Record provides the :inverse_of
option so that you can inform it of these relations. It won't work with: through
, polymorphic
, as
associations. For belongs_to
associations, has_many
inverse associations are ignored.
belongs_to
but with different semantics,has_many
associations,has_and_belongs_to_many
,has_many :through
,class Employee < ActiveRecord::Base has_many :subordinates, :class_name => "Employee", :foreign_key => "manager_id" belongs_to :manager, :class_name => "Employee" end
NULL
in SQL wasting space, while in NoSQL they won't,JOIN
s in NoSQL, but documents can be embedded within another document,attr_accessible
in model, we use params.requir(:model).permit(:name, :email)
, it protects you from unexpected mass assignment,pushState
support, clicking a link causes an AJAX request, updates the URL with pushState
(so back button works) and uses JS to update the <title>
and the <body>
in the DOM, so no CSS and JS are needed to be redownloaded; with Turbolinks page load events won't fire when user navigates from page to page, because DOM never actually reloads, so new events are added (fetch, change, load, restore),class Milestone < ActiveRecord::Base has_many :todos end class Todo < ActiveRecord::Base belongs_to :milestone, :touch => true end # app/views/milestones/show.html.erb <% cache @milestone do %> <h1><%= @milestone.name %></h1> <div class="description"><%= @milestone.description %></div> <ul class="todos"> <%= render @milestone.todos %> </ul> <% end %> # app/views/todos/_todo.html.erb <% cache todo do %> <li class="todo"> <%= todo.description %> <span class="status"><%= todo.status %></span> </li> <% end %>
:touch
option is required for this caching stratego to work properly. If a Todo is added to a milestone, we need to break cache on the milestone to avoid serving stale views. Suppose we have amilestone with ten todos. Editing only one todo causes the milestone's cache to break, but when generating the HTML, all but one of the todo partials can be fetched from the cache, improving render times. We're trading time for space, as this generates a lot of cruft in our cache,
ActionController::Live
- new module provides the ability to stream data to clients, this needs a threaded server (thin, puma) so that stream data actioms from streaming controllers run in a separate thread,Article.all
is now ActiveRecord::Relation
- lazy loaded,Article.none
returns Relation with no records,Article.load
will trigger the SQL query but will return a relation - useful if we want to execute the query for caching behaviour but still need a relation,not
on a where
scope to invert it: >> Article.where.not(name: "Hello") Article Load (107.6ms) SELECT "articles".* FROM "articles" WHERE ("articles"."name" != 'Hello')
order
clause now accepts a hash,find_by
method which works like find_by_something
but doesn't rely on method_missing
and is faster: >> Article.find_by name: "Hello"
ActiveModel::Model
- works like ActiveRecord model but without the database stuff, we can add validations, attr_accessors
,update
action now responds to either PUT
or PATCH
request,concerns
directory. It is designed for modules that we can include in our models to help reduce their size,collection_check_boxes
and collection_radio_buttons
helper methods to generate lists of checkboxes or radio buttons for an association,constraints
to a route will make them default attributes when generating the URL: get 'foo', to: 'articles#index', constraints: {protocol: "https", subdomain: "test"} >> app.foo_url => "https://test.example.com/foo"
hstore
_url
methods generate entire URL, including protocol and domain, _path
generates just the path part.
message-passing
style of programming, where objects of various types define a common interface of operations for user,class Bird def initialize(name) @name = name end def speak puts 'Tweet' end def fly puts 'Up up and away...' end end class Duck < Bird def speak puts "Quack I am #{@name}" end end class Penguin < Bird def speak puts "Squak I am #{@name}" end def fly puts 'Nope. I swim...' end end
And now we can do something like this:
birds = [Bird.new('Tweetie'), Duck.new('Donald'), Penguin.new('Mumbo')] birds.each do |b| b.fly end # Up up and away... # Up up and away... # Nope. I swim... birds.each do |b| b.speak end # Tweet # Quack I am Donald # Squak I am Mumbo
class Duck def speak puts "Quack I am #{@name}" end def fly puts 'Up up and away...' end end class Penguin def speak puts "Squak I am #{@name}" end def fly puts 'Nope. I swim...' end end
We put any shared methods in a module and then include them in our classes:
module Bird def initialize(name) @name = name end end class Duck include Bird ... end class Penguin include Bird ... end
http://www.runtime-era.com/2012/08/polymorphism-in-ruby.html
TODO:
def get_number_digits(number) digits = [] modulo = 1 loop do modulo *= 10 digits << (number % modulo) / (modulo / 10) break if number < modulo end digits end def assemble_digits(ary) number = 0 multiplier = 1 ary.reverse.each do |digit| number += digit * multiplier multiplier *= 10 end number end digits = get_number_digits(1923434) assemble_digits(digits)
ARRAY = [1, 1, 1, 1, 5, 6, 8, 9, 10, 11] def binary_search(ary, number) low_index = 0 high_index = ary.size - 1 while low_index <= high_index mid_index = (low_index + high_index) / 2 value = ary[mid_index] if value < number low_index = mid_index + 1 elsif value > number high_index = mid_index - 1 else return mid_index end end nil end puts binary_search(ARRAY, 1)
Notes:
Queue
- entities in the collection are kept in order and the principal (or only) operations on the collection are the addition of entities to the rear terminal position, known as enqueue, and removal of entities from the front terminal position, known as dequeue. This makes the queue a First-In-First-Out (FIFO) data structure. In a FIFO data structure, the first element added to the queue will be the first one to be removed. This is equivalent to the requirement that once a new element is added, all elements that were added before have to be removed before the new element can be removed. Often a peek or front operation is also implemented, returning the value of the front element without dequeuing it. A queue is an example of a linear data structure, or more abstractly a sequential collection,Stack
- principal (or only) operations on the collection are the addition of an entity to the collection, known as push and removal of an entity, known as pop.[1] The relation between the push and pop operations is such that the stack is a Last-In-First-Out (LIFO) data structure. In a LIFO data structure, the last element added to the structure must be the first one to be removed. This is equivalent to the requirement that, considered as a linear data structure, or more abstractly a sequential collection, the push and pop operations occur only at one end of the structure, referred to as the top of the stack. Often a peek or top operation is also implemented, returning the value of the top element without removing it,# From https://github.com/mruby/mruby/blob/master/benchmark/fib39.rb def fib n return n if n < 2 fib(n-2) + fib(n-1) end puts fib(39)
# explicit SELECT * FROM employee CROSS JOIN department; # implicit SELECT * FROM employee, department;
# explicit SELECT * FROM employee INNER JOIN department ON employee.DepartmentID = department.DepartmentID; # implicit SELECT * FROM employee, department WHERE employee.DepartmentID = department.DepartmentID;
SELECT * FROM employee NATURAL JOIN department;
SELECT * FROM employee LEFT OUTER JOIN department ON employee.DepartmentID = department.DepartmentID;
SELECT * FROM employee RIGHT OUTER JOIN department ON employee.DepartmentID = department.DepartmentID;
SELECT * FROM employee FULL OUTER JOIN department ON employee.DepartmentID = department.DepartmentID;
SELECT F.EmployeeID, F.LastName, S.EmployeeID, S.LastName, F.Country FROM Employee F INNER JOIN Employee S ON F.Country = S.Country WHERE F.EmployeeID < S.EmployeeID ORDER BY F.EmployeeID, S.EmployeeID;
There are several described in RFC2616 for HTTP/1.1:
OPTIONS
allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval,GET
means retrieve whatever information (in the form of an entity) is identified by the Request-URI,HEAD
is identical to GET except that the server MUST NOT return a message-body in the response,POST
is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line,PUT
requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server,DELETE
requests that the origin server delete the resource identified by the Request-URI,TRACE
is used to invoke a remote, application-layer loop-back of the request message,CONNECT
method is reserved for use with a proxy that can dynamically switch to being a tunnel.HTTP Verb | Path | Action |
---|---|---|
GET | /photos | index |
GET | /photos/new | new |
POST | /photos | create |
GET | /photos/:id | show |
GET | /photos/:id/edit | edit |
PATCH/PUT | /photos/:id | update |
DELETE | /photos/:id | destroy |
Forms in web browsers can only send POST
and GET
requests. Any other request (PUT
, DELETE
) is send as a POST
request with a hidden field called _method
, set either to put
or delete
. Rails application detects it and route the request to update
or destroy
action respectively.
XMLHttpRequestObject
, XHR, API available in all modern browsers, that allows JavaScript code on the browser to exchange data with the server and use it to change the user interface of the application on the fly, without a page refresh,class Object def tap yield self self end end
It allows you do do something with an object inside of a block, and always have that block return the object itself.
include
makes the module's methods available to the instance of a class, included module is added as a superclass of the class being defined: module Logger def log(msg) STDERR.puts Time.now.strftime("%H:%M:%S: ") + "#{self} (#{msg})" end end class Song include Logger end s = Song.new s.log("created")
extend
makes the methods available to the class itself: module Humor def tickle "#{self} says hee, hee!" end end obj = "Grouchy" obj.extend Humor obj.tickle # => "Grouchy says hee, hee!"
.find
, if passed an ID or array of ID's will try to find them all. Failing that, it will raise an exception,.find_by_id
is a dynamically generated method (hence slower) and if it doesn't find given records, returns nil
.There are a couple of criteria that should be considered when evaluating a gem:
This is achieved by:
Klass.new
and Klass.allocate
private,Klass.inherited(sub_klass)
and Klass.clone()
to ensure that the Singleton properties are kept when inherited and cloned,Klass.instance()
method that returns the same object each time it is called,Klass._load(str)
to call Klass.instance()
,Klass#clone
and Klass#dup
to raise TypeErrors
to prevent cloning or duping.
: http://www.ruby-doc.org/stdlib-2.1.1/libdoc/observer/rdoc/Observable.html, https://github.com/ruby/ruby/blob/trunk/lib/observer.rb
20:36 < jastix> sqbell: i was asked this kind of question only once. I was asked to think how to sort an array. 20:36 < dhoss> sqbell: do you know anything about basic data structures? sorting, searching trees, etc 20:36 < terrellt> sqbell: I'm not sure what they'd ask. If they're trying to find someone with a classic CS degree you're looking at hash tables, linked lists, dynamic arrays. Sorting algorithms. Most of that's dealt with in Ruby though. 20:37 < terrellt> Search trees are potentially useful in Rails land. Hash tables too. 20:38 < brownies> sqbell: at a bare minimum you should know your way around Hashes and Arrays (duh) and i'd also like to see knowledge of handy Ruby things like OpenStruct 20:39 < brownies> sqbell: from there it really depends on who is interviewing you and the company, but i would be mildly satisfied if you could then go on to talk about calling #map and #reduce on both of those things and how those work, and maybe i would make you write some basic algorithms using those methods 20:37 < joshuawscott> sqbell: if it's a Rails position, you'll likely be asked what you don't like about RoR. 20:40 < brownies> and perhaps since it's an RoR position that's a nice segue into talking about activeRecord things and DB structures 20:42 < jastix> sqbell: often people ask about fibonacci numbers and prime numbers 20:43 < joshuawscott> sqbell: http://codility.com/train/ is a good place to see what you don't know