Week 04, Day 05
What we covered today:
- Demos
- Object Oriented Programming
- Objects
- Classes
- Active Record
- Associations
Object Oriented Programming (OOP)
Brief Intro to OOP
Basically, OOP is an approach to development that tries to replicate real life. It is pretty much always done using objects or classes as namespaces and treats them as a way to make your code "modular".
Small, clean methods are also a major part of it.
Basic OOP in Ruby
Up until now, we have only been modifying objects. We can create an empty one and then add methods and features to construct a particular one.
o = {}
def o.silly_method
puts "I'm silly!"
end
This is not ideal. It means we always have to rewrite a whole bunch of code.
To get around this we use classes!
Classes
Everything in Ruby inherits from a class (is an instance of a class) and in some capacity inherit from Object. This doesn't pop up that regularly, but to see what I mean...
{}.class
[].class
"".class
# etc.
# If you want to see everything a data type inherits from...
{}.class.ancestors
[].class.ancestors
"".class.ancestors
Treat classes as a factory or a blueprint, something that gives another thing all the details that it needs. We use them to stop duplicate code, make manageable, easy-to-debug code.
What do they look like?
class Person
end
The Real Power...
...Comes from being able to add methods! We can encapsulate functionality with classes.
class Person
def speak
end
def laugh
end
end
Unlike javascript though, we can't call the functions or methods on the class itself. This is how we could do it in javascript...
var Person = {
talk: function () {
console.log( "I can talk!" );
}
}
Person.talk();
In Ruby, we need to create an instance of the class. Think of the class itself being a blueprint, and the instance being the house that is built from it (it is the thing with all the power and functionality).
class Person
def speak
puts "Speak"
end
def laugh
end
end
person = Person.new
person.speak # Will work!
Obviously, though. We want to be able to store something on the person themselves! Maybe we want to know their name, age or gender for example.
We use what are called "getters" and "setters" to do this.
class Person
def name=(name)
@name = name
end
def name
@name
end
def age=(age)
@age = age
end
def age
@age
end
end
person = Person.new
person.name=( "Jane" )
person.age=( 80 )
person.name # Will return "Jane"
person.age # Will return 80
Surely, you can all see the duplication though. There must be an easier way! There is.
class Person
attr_accessor :name, :age
end
person = Person.new
person.name = "Serge"
person.name # Will return "Serge"
person.age = 100
person.age # Will return 100
But it is a hassle to define each one of these like this. We are lucky to have a solution for this.
On a creation of a new instance in Ruby, when the .new method is called, it will also call automatically an initialize method.
class Person
attr_accessor :name, :age
def initialize( name, age )
@name = name
@age = age
end
end
This will do everything that we were doing before.
We can add heaps of methods and see what we have to work with as well.
class Person
def method_one
end
def method_two
end
def method_three
end
end
person = Person.new
# To access all the methods:
person.class.instance_methods
# If you pass in the value false to the instance methods method, it will only show you the methods you have defined.
person.class.instance_methods( false )
Active Record
Preface - Object Relational Mapping
One of the most signifcant principles in Object-Oriented Programming is the idea of rich objects - things that store data, and allow it to be retrieved in logical and concise ways. This pattern is what Active Record strives for. They call it Object Relational Mapping, or ORM. It's basic principle is that rich objects in your application should be connected with database tables. Using ORM, the properties and relationships of the objects in an application can be easily stored and retrieved from a database without writing SQL statements directly and with less overall database access code. Also is far more secure due to lessened risks in regards to SQL Injections.
What does Active Record do?
Active Record, as an ORM, gives us several mechanisms, the most important being the ability to:
- Represent models and their data.
- Represent associations between these models.
- Represent inheritance hierarchies through related models.
- Validate models before they get persisted to the database.
- Perform database operations in an object-oriented fashion.
Convention over Configuration
Convention over configuration is big in development anyway, but it is particularly full on when it comes to Active Record. That is because it uses the names to sort out assosciations etc. Make sure you follow these rules!!
- Database Tables - Plural with underscores seperating words (articles, line_items etc.)
- Model / Class Names - Singular with the first letter of each word capitalized (Article, LineItem etc.)
How do you work with Active Record?
Well, at the end of the day, Active Record is a gem like any other. So we need to require it!!
require 'active_record' # make sure this is at the top of the file!
After requiring it, we also need to actually establish the connection to the database. These are annoying lines that will be done automatically in Rails! Just copy and paste them.
# Sets up our connection to the database.db we have created
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'database.db'
)
ActiveRecord::Base.logger = Logger.new(STDERR) # Logs out the Active Record generated SQL in the terminal. This isn't necessary but very helpful and cool to see what it is actually running
Brief Interlude of Random Stuff
SQL | HTTP Verbs | Active Record | |
---|---|---|---|
Create | INSERT | (Put) or POST | .create or .new/.save |
Read | SELECT | GET | .find or .find_by |
Update | UPDATE | (Patch) or POST | .update or .find/.save |
Delete | DELETE | (Delete) or POST or GET | .destroy or .destroy_all |
CRUD with Active Record
Once we have our models defined (i.e. our classes), we can get into the CRUD stuff. There are always a lot of options of how to do this!
Create
plant = Plant.new
plant.name = "Hibiscus"
plant.flowers = true
plan.save # YOU MUST RUN THIS LINE! (When using the new approach)
# OR
plant.create( :name => "Hibiscus", :flowers => true )
# OR
user = User.new do |u|
u.name = "David"
u.occupation = "Code Artist"
end # THIS WILL RUN THE SAVE AUTOMATICALLY
Read
Plant.all
Plant.first
Plant.last
Plant.find( 10 ) # Find with an ID
Plant.find_by( :name => "Hibiscus" ) # Returns the first plant that this works with
Plant.where( :name => "Hibiscus" ) # Returns all instances where this is appropriate
Update
plant = Plant.find_by( :name => "Hibiscus" )
plant.name = "Hibiscus 2"
plant.save
plant = Plant.find_by( :name => 'Hibiscus 2' )
plant.update( :name => 'Hibiscus' ) # This will save automatically
Delete
plant = Plant.find_by( :name => 'Hibiscus' )
plant.destroy
Plant.destroy_all
For the basics of Active Record, see here.
For more ways to query things with Active Record, see here.