rails israel 2013
DESCRIPTION
Active Record 4.0 includes all sorts of exciting support for PostgreSQL! In this presentation, I show many of these improvements, and discuss why these are important for Web developers. If you haven't yet adopted PostgreSQL, now might be a great time and chance to do so.TRANSCRIPT
![Page 1: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/1.jpg)
Integrating PostgreSQLReuven M. Lerner • [email protected]
Rails Israel Conference 2013October 9th, 2013 • Tel Aviv
1
![Page 2: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/2.jpg)
Who am I?
• Web developer since 1993
• Linux Journal columnist since 1996
• PostgreSQL user since at least 1999
• Ruby/Rails developer, teacher since 2005
• PhD candidate in learning sciences at Northwestern University
2
![Page 3: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/3.jpg)
But before all of that...I was a kid.
3
![Page 4: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/4.jpg)
(Yes, a geeky kid.)
But before all of that...I was a kid.
3
![Page 5: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/5.jpg)
4
![Page 6: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/6.jpg)
5
![Page 7: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/7.jpg)
6
![Page 8: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/8.jpg)
7
![Page 9: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/9.jpg)
Databases?!? Who needs them?
8
![Page 10: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/10.jpg)
Er, I did. And do.
• Whadaya know? Databases are useful!
• They offer an abstraction layer for our data
• I don’t care how it is stored, so long as the data is safe, and I can get it back
9
![Page 11: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/11.jpg)
What is a database?
Database
Store data confidently
Retrieve data flexibly
10
![Page 12: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/12.jpg)
Relational databases
• Client-server
• All data is stored in two-dimensional tables
• Client-server communication is in SQL
• SQL is standard. Each database implements a superset of a subset of that standard.
11
![Page 13: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/13.jpg)
“Impedance mismatch”
• You basically couldn’t find two languages that are more different from one another than Ruby and SQL:
• Ruby — object oriented, imperative
• SQL — table oriented, declarative
• Mixing them is ugly, as we all know
12
![Page 14: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/14.jpg)
ActiveRecord!
• ActiveRecord is the default ORM (object-relational manager) in Rails
• ActiveRecord translates our Ruby method calls to SQL queries
• Query results are turned into Ruby objects and collections
13
![Page 15: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/15.jpg)
The good news...
• ActiveRecord papers over the differences between databases!
14
![Page 16: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/16.jpg)
... and the bad news
• ActiveRecord papers over the differences between databases!
15
![Page 17: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/17.jpg)
The really bad news
• This means we’re:
• Writing extra code
• Slowing down our apps
• Putting logic in the wrong place
• Database logic shouldn’t be in your app (just like SQL shouldn’t be in your HTML)
16
![Page 18: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/18.jpg)
PostgreSQL
17
![Page 19: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/19.jpg)
PostgreSQL
17
![Page 20: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/20.jpg)
PostgreSQL
17
![Page 21: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/21.jpg)
PostgreSQL
17
![Page 22: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/22.jpg)
PostgreSQL
17
![Page 23: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/23.jpg)
PostgreSQL
17
![Page 24: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/24.jpg)
PostgreSQL
• Very fast, very scalable.
• Amazingly flexible, easily extensible.
• Rock-solid — no crashes, corruption, major security issues for years
• Ridiculously easy administration
• It also happens to be free (MIT/BSD)
18
![Page 25: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/25.jpg)
Brief history
• Ingres (Stonebreaker, Berkeley)
• Postgres (Stonebreaker, Berkeley)
• PostgreSQL project = Postgres + SQL
• About one major release per year
• Latest and greatest is 9.3
19
![Page 26: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/26.jpg)
Features!
• MVCC
• Custom data types
• CTEs
• Functional, partial indexes
• Full-text search
• Server-side functions
20
![Page 27: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/27.jpg)
Rails 4 to the rescue!
• As of Rails 4, many PostgreSQL features are supported, out of the box, by ActiveRecord
• The best of both worlds: Amazing database features, and we don’t have to use SQL!
21
![Page 28: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/28.jpg)
But wait!
• What if I want to switch to another database?
• Won’t I lose the platform independence?
• Yes, you will.
• Fortunately, this will almost certainly not happen.
• And if it does, it’ll be super-painful anyway.
22
![Page 29: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/29.jpg)
Wait again!
• This means that I can’t use SQLite in development, and PostgreSQL on the server.
• Yes, that’s right.
• You should use the same database in development and production. Please.
23
![Page 30: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/30.jpg)
Data types
• Databases are like programming languages: The right choice of data type can have huge consequences in speed, size, and flexibility
• PostgreSQL has some special ones that can really come in handy!
24
![Page 31: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/31.jpg)
Network types
• PostgreSQL supports INET, CIDR, and MACADDR types
• If you’re ever storing network addresses, you should use these, not a text type!
• Savings in space (and time), and faster comparisons (numeric vs. textual)
25
![Page 32: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/32.jpg)
CREATE TABLE Logins (
id SERIAL PRIMARY KEY,
local_ip INET NOT NULL,
remote_ip INET NOT NULL);
INSERT INTO Logins (local_ip, remote_ip) values ('192.168.1.1', '10.1.2.3');
reuven=# INSERT INTO Logins (local_ip, remote_ip) values ('999.999.999.1000', '10.1.2.3');
ERROR: invalid input syntax for type inet: "999.999.999.1000"
LINE 1: INSERT INTO Logins (local_ip, remote_ip) values ('999.999.99...
26
![Page 33: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/33.jpg)
Rails 4 migrations$ rails g model login local_ip:inet remote_ip:inet
class CreateLogins < ActiveRecord::Migration
def change
create_table :logins do |t|
t.inet :local_ip, nulls: :false
t.inet :remote_ip, nulls: :false
t.timestamps
end
end
end
27
![Page 34: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/34.jpg)
Rails 4 appLogin.create!(local_ip:'192.168.1.1', remote_ip:'10.1.2.3')
Login.create!(local_ip:'192.168.1.1000', remote_ip:'10.1.2.3')
IPAddr::InvalidAddressError: invalid address
i = Login.first.local_ip
i.class
=> IPAddr
i.ipv4?
=> true
i.ipv6?
=> false
28
![Page 35: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/35.jpg)
Managing a network?
• We also have CIDR and MAC addresses!
Netconfig.create!(net: '192.168.1.1/255.255.255.0')
29
![Page 36: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/36.jpg)
UUIDs
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE evil_companies (
id UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v1(),
name TEXT);
INSERT INTO evil_companies (name) VALUES ('HOT Internet');
INSERT INTO evil_companies (name) VALUES ('HOT TV');
INSERT INTO evil_companies (name) VALUES ('HOT Telephone');
INSERT INTO evil_companies (name) VALUES ('Voldemort, Ltd.');
30
![Page 37: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/37.jpg)
Wait... extensions?
• Yes, PostgreSQL has extensions!
• Download and install them from PGXN
• (Think of them as gems for PostgreSQL)
• Some, like uuid-ossp, come by default
• Activate them with CREATE EXTENSION
31
![Page 38: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/38.jpg)
32
![Page 39: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/39.jpg)
With this in place...
SELECT * FROM evil_companies ;
┌──────────────────────────────────────┬─────────────────┐
│ id │ name │
├──────────────────────────────────────┼─────────────────┤
│ da7a333a-3069-11e3-b4fd-28cfe91f81e7 │ HOT Internet │
│ dc480be2-3069-11e3-a0f4-28cfe91f81e7 │ HOT TV │
│ ddadabfe-3069-11e3-af1d-28cfe91f81e7 │ HOT Telephone │
│ 4c33a128-306a-11e3-8000-28cfe91f81e7 │ Voldemort, Ltd. │
└──────────────────────────────────────┴─────────────────┘
33
![Page 40: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/40.jpg)
Migrations
# Requires some manual tinkering
class CreateEvilCompanies < ActiveRecord::Migration
def change
enable_extension "uuid-ossp"
create_table :evil_companies, id: :uuid do |t|
t.text :name
t.timestamps
end
end
end
34
![Page 41: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/41.jpg)
Migrations
# Requires some manual tinkering
class CreateEvilCompanies < ActiveRecord::Migration
def change
enable_extension "uuid-ossp"
create_table :evil_companies, id: :uuid do |t|
t.text :name
t.timestamps
end
end
end
34
![Page 42: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/42.jpg)
In the app
EvilCompany.create!(name: 'HOT Internet')
EvilCompany.first.id
=> "07d3f537-c008-4006-9f2b-0592c7df3ebf"
35
![Page 43: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/43.jpg)
JSON
• Remember the “impedance mismatch” between objects and databases?
• Now we have another one — we get data in JSON, turn it into database objects, and then turn it back into JSON
• But now PostgreSQL can store, retrieve, and query JSON!
36
![Page 44: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/44.jpg)
CREATE TABLE bad_service (
id SERIAL,
info JSON
);
INSERT INTO bad_service (info)
values ('{"company":"HOT Internet", "badness":9}');
INSERT INTO bad_service (info) VALUES ('la la la');
ERROR: invalid input syntax for type json
LINE 1: insert into bad_service (info) values ('la la la');
^
DETAIL: Token "la" is invalid.
CONTEXT: JSON data, line 1: la...
37
![Page 45: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/45.jpg)
JSON operators!
• As of PostgreSQL 9.3, we get operators
• Work with JSON, much like XML:SELECT info#>'{company}' from bad_service;┌────────────────┐│ ?column? │├────────────────┤│ "HOT Internet" │└────────────────┘
38
![Page 46: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/46.jpg)
Migration
$ rails g model bad_service info:json
class CreateBadServices < ActiveRecord::Migration
def change
create_table :bad_services do |t|
t.json :info
t.timestamps
end
end
end
39
![Page 47: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/47.jpg)
App
BadService.create!(info: '{"company":"HOT Internet"}')
b = BadService.first
b.info.class
=> Hash
b.info['company']
=> "HOT Internet"
40
![Page 48: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/48.jpg)
Ranges
CREATE TABLE forecast (
forecast_for DATE,
hi_lo int4range);
INSERT INTO forecast (forecast_for, hi_lo) VALUES ('8-oct-2013', '[19,29]');
INSERT INTO forecast (forecast_for, hi_lo) VALUES ('9-oct-2013', '[20,32]');
INSERT INTO forecast (forecast_for, hi_lo) VALUES ('10-oct-2013', '[19,28]');
41
![Page 49: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/49.jpg)
SELECT * FROM forecast ;
┌──────────────┬─────────┐
│ forecast_for │ hi_lo │
├──────────────┼─────────┤
│ 2013-10-08 │ [19,30) │
│ 2013-10-09 │ [20,33) │
│ 2013-10-10 │ [19,29) │
└──────────────┴─────────┘
SELECT * FROM forecast WHERE hi_lo @> 19;
┌──────────────┬─────────┐
│ forecast_for │ hi_lo │
├──────────────┼─────────┤
│ 2013-10-08 │ [19,30) │
│ 2013-10-10 │ [19,29) │
└──────────────┴─────────┘
42
![Page 50: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/50.jpg)
Migration$ model forecast forecast_for:date hi_lo:int4range
class CreateForecasts < ActiveRecord::Migration
def change
create_table :forecasts do |t|
t.date :forecast_for
t.int4range :hi_lo
t.timestamps
end
end
end
43
![Page 51: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/51.jpg)
Ranges in apps
Forecast.create!(forecast_for: Time.now, hi_lo: 20..30)
Forecast.first.hi_lo
=> 20...31
Forecast.first.hi_lo.class
Range
44
![Page 52: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/52.jpg)
Arrays!
CREATE TABLE posts (
body text,
tags text[]
);
INSERT INTO posts (body, tags) VALUES ('Hello, world', '{intro, hello}');
INSERT INTO posts (body, tags) VALUES ('Hello again', '{DRY, boring, hello}');
45
![Page 53: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/53.jpg)
SELECT * FROM posts;
┌──────────────┬────────────────────┐
│ body │ tags │
├──────────────┼────────────────────┤
│ Hello, world │ {intro,hello} │
│ Hello again │ {DRY,boring,hello} │
└──────────────┴────────────────────┘
SELECT * from posts where 'intro' = ANY(tags);
┌──────────────┬───────────────┐
│ body │ tags │
├──────────────┼───────────────┤
│ Hello, world │ {intro,hello} │
└──────────────┴───────────────┘
46
![Page 54: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/54.jpg)
Arrays in migrations$ rails g model post body:text tags:text
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.text :body
t.text :tags, array: true
t.timestamps
end
end
end
47
![Page 55: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/55.jpg)
Arrays in your app
Post.create!(body: 'First post!', tags: %w(first second third))
Post.first.tags
=> ["first", "second", "third"]
48
![Page 56: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/56.jpg)
Normalization = DRY
• Everyone loves to talk about tagging as a great example of PostgreSQL arrays
• Um... don’t forget about normalization, the DRY of database design
• I know, it’s not cool to talk about it in the Age of NoSQL. But it really does work.
49
![Page 57: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/57.jpg)
Premature denormalization is the
root of all database evil.
50
![Page 58: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/58.jpg)
Hstore
• A key-value storage system in PostgreSQL!
• Any column can be defined as hstore
• Then you can query it — and you get a hash back!
• It’s sort of like Redis or memcached, but integrated into (and backed by) Postgres!
51
![Page 59: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/59.jpg)
PostgreSQL
CREATE EXTENSION hstore;
CREATE TABLE posts2 (
body text,
tags hstore
);
INSERT INTO posts2 (body, tags)
VALUES ('Hello, there', '"hello" => 2, "boring" => 10');
INSERT INTO posts2 (body, tags)
values ('Hello again', '"DRY" => 5,
"hello" => 2, "boring" => 3');
52
![Page 60: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/60.jpg)
SELECT * from posts2 where defined(tags, 'hello');
┌──────────────┬─────────────────────────────────────────┐
│ body │ tags │
├──────────────┼─────────────────────────────────────────┤
│ Hello, there │ "hello"=>"2", "boring"=>"10" │
│ Hello again │ "DRY"=>"5", "hello"=>"2", "boring"=>"3" │
└──────────────┴─────────────────────────────────────────┘
(2 rows)
SELECT tags -> 'boring' from posts2 ;
┌──────────┐
│ ?column? │
├──────────┤
│ 10 │
│ 3 │
└──────────┘
(2 rows)
53
![Page 61: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/61.jpg)
And also...
SELECT * FROM posts2
WHERE (tags -> 'boring')::integer > 5;
┌──────────────┬──────────────────────────────┐
│ body │ tags │
├──────────────┼──────────────────────────────┤
│ Hello, there │ "hello"=>"2", "boring"=>"10" │
└──────────────┴──────────────────────────────┘
54
![Page 62: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/62.jpg)
Migration$ rails g model newpost body:text tags:hstore
class CreateNewposts < ActiveRecord::Migration
def change
enable_extension "hstore"
create_table :newposts do |t|
t.text :body
t.hstore :tags
t.timestamps
end
end
end
55
![Page 63: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/63.jpg)
In your app
Newpost.create!(:body => 'foo', :tags => {a:1, b:2})
Newpost.first.tags
=> {"a"=>"1", "b"=>"2"} # hash!
Newpost.first.tags['a']
=> "1" # now a string!
Newpost.first.tags[:a]
=> nil # not WithIndifferentAccess!
56
![Page 64: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/64.jpg)
Indexes
• PostgreSQL offers different index types
• Specify these in your migrations
• Partial indexes (with a WHERE clause)
• Functional indexes (apply a function!)
• Full-text indexes (many languages)
• Geospatial indexes (install PostGIS!)
57
![Page 65: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/65.jpg)
Some things are still missing
• “The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used.”
• — Rails Guide, ActiveRecord Migrations
58
![Page 66: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/66.jpg)
Sigh.
59
![Page 67: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/67.jpg)
60
![Page 68: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/68.jpg)
Your data are your crown jewels!
61
![Page 69: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/69.jpg)
Summary
• Rails good! PostgreSQL good!
• Rails + PostgreSQL = Doubleplus good!
• Rails 4 + PostgreSQL = Secure storage, flexible retrieval, and a lot of geeky DB fun!
62
![Page 70: Rails israel 2013](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f490cb4c905524c8b4823/html5/thumbnails/70.jpg)
Thanks!(Any questions?)
[email protected]://www.lerner.co.il/
@reuvenmlerner
054-496-8405“reuvenlerner” on Skype
63