surus 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +17 -0
- data/CHANGELOG.md +5 -0
- data/README.md +37 -19
- data/Rakefile +10 -0
- data/gemfiles/4.0.gemfile +5 -0
- data/gemfiles/4.1.gemfile +5 -0
- data/gemfiles/4.2.gemfile +5 -0
- data/lib/surus/json/query.rb +28 -2
- data/lib/surus/version.rb +1 -1
- data/spec/database.yml +1 -0
- data/spec/database.yml.travis +5 -0
- data/spec/database_structure.sql +22 -1
- data/spec/factories.rb +15 -0
- data/spec/json/json_spec.rb +45 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/synchronous_commit/model_spec.rb +5 -5
- data/surus.gemspec +2 -1
- metadata +25 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd4bbbf8c57e01cfb2627d2b0a3913006f1432c7
|
4
|
+
data.tar.gz: 73504b7836c48ee52f43d62f91d93eeb0f52e733
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce79c2dabce1600a7a3503f992cd735e7eac90fd59c65ad557ee5a55e58cde0eb0eebfb91a8be97456479ca31c0efe88375b3cc0e8ae8f9ac7e699faf7b8cef0
|
7
|
+
data.tar.gz: 498d6d89a59f5c2d49d0119a94ec76429a7a589f398050ab3a22824264ada83c886017f7e7e9632188fd8f37eccda0f585eeca579610cea9f7a8ab87a00966fa
|
data/.travis.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.2.0
|
4
|
+
- 2.1.5
|
5
|
+
- 2.0.0
|
6
|
+
gemfile:
|
7
|
+
- gemfiles/4.0.gemfile
|
8
|
+
- gemfiles/4.1.gemfile
|
9
|
+
- gemfiles/4.2.gemfile
|
10
|
+
|
11
|
+
addons:
|
12
|
+
postgresql: "9.2"
|
13
|
+
|
14
|
+
before_script:
|
15
|
+
- cp spec/database.yml.travis spec/database.yml
|
16
|
+
- psql -U postgres -c 'create database surus_test;'
|
17
|
+
- psql -U postgres -f spec/database_structure.sql surus_test
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
Surus
|
2
2
|
=====
|
3
3
|
|
4
|
+
[![Build Status](https://travis-ci.org/jackc/surus.svg?branch=master)](https://travis-ci.org/jackc/surus)
|
5
|
+
|
4
6
|
# Description
|
5
7
|
|
6
8
|
Surus adds PostgreSQL specific functionality to ActiveRecord. It adds
|
@@ -14,16 +16,33 @@ substantially faster than converting ActiveRecord objects to JSON.
|
|
14
16
|
|
15
17
|
gem install surus
|
16
18
|
|
17
|
-
Or add to your Gemfile.
|
19
|
+
Or add to your Gemfile. This version of Surus only works on Rails 4.
|
18
20
|
|
19
21
|
gem 'surus'
|
20
22
|
|
21
23
|
## Rails 3
|
22
24
|
|
23
|
-
|
25
|
+
Use the 0.4 line for Rails 3
|
24
26
|
|
25
27
|
gem 'surus', '~> 0.4.2'
|
26
28
|
|
29
|
+
# JSON
|
30
|
+
|
31
|
+
PostgreSQL 9.2 added `row_to_json` and `array_to_json` functions. These
|
32
|
+
functions can be used to build JSON very quickly. Unfortunately, they are
|
33
|
+
somewhat cumbersome to use. The `find_json` and `all_json` methods are easy to
|
34
|
+
use wrappers around the lower level PostgreSQL functions that closely mimic
|
35
|
+
the Rails `to_json` interface.
|
36
|
+
|
37
|
+
User.find_json 1
|
38
|
+
User.find_json 1, columns: [:id, :name, :email]
|
39
|
+
Post.find_json 1, include: :author
|
40
|
+
User.find_json(user.id, include: {posts: {columns: [:id, :subject]}})
|
41
|
+
User.all_json
|
42
|
+
User.where(admin: true).all_json
|
43
|
+
User.all_json(columns: [:id, :name, :email], include: {posts: {columns: [:id, :subject]}})
|
44
|
+
Post.all_json(include: [:forum, :post])
|
45
|
+
|
27
46
|
# Hstore
|
28
47
|
|
29
48
|
Hstores can be searched with helper scopes.
|
@@ -48,6 +67,22 @@ nested data structures can be serialized to an hstore. In other words, any
|
|
48
67
|
hash that can be serialized with the normal Rails YAML serialization can be
|
49
68
|
serialized with Surus.
|
50
69
|
|
70
|
+
**Serialize example (Rails 3+)**:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
class User < ActiveRecord::Base
|
74
|
+
serialize :settings, Surus::Hstore::Serializer.new
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
**Store example (Rails 4+)**:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
class User < ActiveRecord::Base
|
82
|
+
store :settings, accessors: [:session_timeout], coder: Surus::Hstore::Serializer.new
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
51
86
|
# Array
|
52
87
|
|
53
88
|
Arrays can be searched with helper scopes.
|
@@ -79,23 +114,6 @@ entire session or per transaction.
|
|
79
114
|
|
80
115
|
Read more in the [PostgreSQL asynchronous commit documentation](http://www.postgresql.org/docs/9.1/interactive/wal-async-commit.html).
|
81
116
|
|
82
|
-
# JSON
|
83
|
-
|
84
|
-
PostgreSQL 9.2 added `row_to_json` and `array_to_json` functions. These
|
85
|
-
functions can be used to build JSON very quickly. Unfortunately, they are
|
86
|
-
somewhat cumbersome to use. The `find_json` and `all_json` methods are easy to
|
87
|
-
use wrappers around the lower level PostgreSQL functions that closely mimic
|
88
|
-
the Rails `to_json` interface.
|
89
|
-
|
90
|
-
User.find_json 1
|
91
|
-
User.find_json 1, columns: [:id, :name, :email]
|
92
|
-
Post.find_json 1, include: :author
|
93
|
-
User.find_json(user.id, include: {posts: {columns: [:id, :subject]}})
|
94
|
-
User.all_json
|
95
|
-
User.where(admin: true).all_json
|
96
|
-
User.all_json(columns: [:id, :name, :email], include: {posts: {columns: [:id, :subject]}})
|
97
|
-
Post.all_json(include: [:forum, :post])
|
98
|
-
|
99
117
|
# Benchmarks
|
100
118
|
|
101
119
|
JSON generation is with all_json and find_json is substantially faster than to_json.
|
data/Rakefile
CHANGED
data/lib/surus/json/query.rb
CHANGED
@@ -15,7 +15,11 @@ module Surus
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def subquery_sql
|
18
|
-
|
18
|
+
if options.key?(:columns) || options.key?(:include)
|
19
|
+
select(columns.map(&:to_s).join(', ')).to_sql
|
20
|
+
else
|
21
|
+
original_scope.to_sql
|
22
|
+
end
|
19
23
|
end
|
20
24
|
|
21
25
|
def columns
|
@@ -39,10 +43,32 @@ module Surus
|
|
39
43
|
def association_columns
|
40
44
|
included_associations_name_and_options.map do |association_name, association_options|
|
41
45
|
association = klass.reflect_on_association association_name
|
42
|
-
|
46
|
+
|
47
|
+
# The way to get the association type is different in Rails 4.2 vs 4.0-4.1
|
48
|
+
association_type = if defined? ActiveRecord::Reflection::BelongsToReflection
|
49
|
+
# Rails 4.2+
|
50
|
+
case association
|
51
|
+
when ActiveRecord::Reflection::HasOneReflection
|
52
|
+
:has_one
|
53
|
+
when ActiveRecord::Reflection::BelongsToReflection
|
54
|
+
:belongs_to
|
55
|
+
when ActiveRecord::Reflection::HasManyReflection
|
56
|
+
:has_many
|
57
|
+
when ActiveRecord::Reflection::HasAndBelongsToManyReflection
|
58
|
+
:has_and_belongs_to_many
|
59
|
+
end
|
60
|
+
else
|
61
|
+
# Rails 4.0-4.1
|
62
|
+
association.source_macro
|
63
|
+
end
|
64
|
+
|
65
|
+
subquery = case association_type
|
43
66
|
when :belongs_to
|
44
67
|
association_scope = BelongsToScopeBuilder.new(original_scope, association).scope
|
45
68
|
RowQuery.new(association_scope, association_options).to_sql
|
69
|
+
when :has_one
|
70
|
+
association_scope = HasManyScopeBuilder.new(original_scope, association).scope
|
71
|
+
RowQuery.new(association_scope, association_options).to_sql
|
46
72
|
when :has_many
|
47
73
|
association_scope = HasManyScopeBuilder.new(original_scope, association).scope
|
48
74
|
ArrayAggQuery.new(association_scope, association_options).to_sql
|
data/lib/surus/version.rb
CHANGED
data/spec/database.yml
CHANGED
data/spec/database_structure.sql
CHANGED
@@ -62,6 +62,27 @@ CREATE TABLE users(
|
|
62
62
|
|
63
63
|
|
64
64
|
|
65
|
+
DROP TABLE IF EXISTS bios CASCADE;
|
66
|
+
|
67
|
+
CREATE TABLE bios(
|
68
|
+
id serial PRIMARY KEY,
|
69
|
+
body text NOT NULL,
|
70
|
+
website_url varchar NOT NULL,
|
71
|
+
author_id integer NOT NULL REFERENCES users
|
72
|
+
);
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
DROP TABLE IF EXISTS avatars CASCADE;
|
77
|
+
|
78
|
+
CREATE TABLE avatars(
|
79
|
+
id serial PRIMARY KEY,
|
80
|
+
url varchar NOT NULL,
|
81
|
+
author_id integer NOT NULL REFERENCES users
|
82
|
+
);
|
83
|
+
|
84
|
+
|
85
|
+
|
65
86
|
DROP TABLE IF EXISTS forums CASCADE;
|
66
87
|
|
67
88
|
CREATE TABLE forums(
|
@@ -98,4 +119,4 @@ CREATE TABLE posts_tags(
|
|
98
119
|
post_id integer NOT NULL REFERENCES posts,
|
99
120
|
tag_id integer NOT NULL REFERENCES tags,
|
100
121
|
PRIMARY KEY (post_id, tag_id)
|
101
|
-
);
|
122
|
+
);
|
data/spec/factories.rb
CHANGED
@@ -17,5 +17,20 @@ FactoryGirl.define do
|
|
17
17
|
factory :user, aliases: [:author] do
|
18
18
|
name { Faker::Internet.user_name }
|
19
19
|
email { Faker::Internet.email }
|
20
|
+
after(:build) do |user|
|
21
|
+
user.bio ||= FactoryGirl.build(:bio, author: user)
|
22
|
+
user.avatar ||= FactoryGirl.build(:avatar, author: user)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
factory :bio do
|
27
|
+
author
|
28
|
+
body { Faker::Lorem.paragraph }
|
29
|
+
website_url { Faker::Internet.url }
|
30
|
+
end
|
31
|
+
|
32
|
+
factory :avatar do
|
33
|
+
author
|
34
|
+
url { Faker::Avatar.image }
|
20
35
|
end
|
21
36
|
end
|
data/spec/json/json_spec.rb
CHANGED
@@ -22,6 +22,15 @@ describe 'json' do
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
context 'when scope chain has select' do
|
26
|
+
it 'uses the select for json' do
|
27
|
+
user = FactoryGirl.create :user
|
28
|
+
to_json = Oj.load user.to_json only: [:id, :name]
|
29
|
+
find_json = Oj.load User.select("id, name").find_json(user.id)
|
30
|
+
expect(find_json).to eq(to_json)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
25
34
|
context 'when scope chain has a joins with ambiguous column names' do
|
26
35
|
it 'works' do
|
27
36
|
user = FactoryGirl.create :user
|
@@ -60,6 +69,33 @@ describe 'json' do
|
|
60
69
|
expect(find_json).to eq(to_json)
|
61
70
|
end
|
62
71
|
|
72
|
+
it 'includes entire has_one object' do
|
73
|
+
user = FactoryGirl.create :user
|
74
|
+
to_json = Oj.load user.to_json(include: :bio)
|
75
|
+
find_json = Oj.load User.find_json(user.id, include: :bio)
|
76
|
+
expect(find_json).to eq(to_json)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'filters by has_one conditions' do
|
80
|
+
user = FactoryGirl.create :user
|
81
|
+
find_json = Oj.load User.find_json(user.id, include: :bio_with_impossible_conditions)
|
82
|
+
expect(find_json.fetch('bio_with_impossible_conditions')).to be_nil
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'includes multiple entire has_one objects' do
|
86
|
+
user = FactoryGirl.create :user
|
87
|
+
to_json = Oj.load user.to_json(include: [:bio, :avatar])
|
88
|
+
find_json = Oj.load User.find_json(user.id, include: [:bio, :avatar])
|
89
|
+
expect(find_json).to eq(to_json)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'includes only selected columns of has_one object' do
|
93
|
+
user = FactoryGirl.create :user
|
94
|
+
to_json = Oj.load user.to_json(include: {bio: {only: [:id, :body]}})
|
95
|
+
find_json = Oj.load User.find_json(user.id, include: {bio: {columns: [:id, :body]}})
|
96
|
+
expect(find_json).to eq(to_json)
|
97
|
+
end
|
98
|
+
|
63
99
|
it 'includes entire has_many association' do
|
64
100
|
user = FactoryGirl.create :user
|
65
101
|
posts = FactoryGirl.create_list :post, 2, author: user
|
@@ -168,5 +204,14 @@ describe 'json' do
|
|
168
204
|
all_json = Oj.load User.all_json
|
169
205
|
expect(all_json).to eq(to_json)
|
170
206
|
end
|
207
|
+
|
208
|
+
context 'when scope chain has select' do
|
209
|
+
it 'uses the select for json' do
|
210
|
+
users = FactoryGirl.create_list :user, 3
|
211
|
+
to_json = Oj.load users.to_json only: [:id, :name]
|
212
|
+
all_json = Oj.load User.select("id, name").all_json
|
213
|
+
expect(all_json).to eq(to_json)
|
214
|
+
end
|
215
|
+
end
|
171
216
|
end
|
172
217
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -30,6 +30,21 @@ class User < ActiveRecord::Base
|
|
30
30
|
|
31
31
|
# association name is reserved word in PostgreSQL
|
32
32
|
has_many :rows, foreign_key: :author_id, class_name: 'Post', table_name: 'posts'
|
33
|
+
|
34
|
+
has_one :bio, foreign_key: :author_id
|
35
|
+
has_one :avatar, foreign_key: :author_id
|
36
|
+
has_one :bio_with_impossible_conditions,
|
37
|
+
-> { where '1=2' },
|
38
|
+
foreign_key: :author_id,
|
39
|
+
class_name: 'Bio'
|
40
|
+
end
|
41
|
+
|
42
|
+
class Bio < ActiveRecord::Base
|
43
|
+
belongs_to :author, class_name: 'User'
|
44
|
+
end
|
45
|
+
|
46
|
+
class Avatar < ActiveRecord::Base
|
47
|
+
belongs_to :author, class_name: 'User'
|
33
48
|
end
|
34
49
|
|
35
50
|
class Forum < ActiveRecord::Base
|
@@ -2,18 +2,18 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Surus::SynchronousCommit::Model do
|
4
4
|
let(:conn) { ActiveRecord::Base.connection }
|
5
|
-
|
5
|
+
|
6
6
|
describe "synchronous_commit" do
|
7
7
|
it "is delegated to connection" do
|
8
|
-
conn.
|
8
|
+
expect(conn).to receive(:synchronous_commit)
|
9
9
|
ActiveRecord::Base.synchronous_commit
|
10
10
|
end
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
describe "synchronous_commit=" do
|
14
14
|
it "is delegated to connection" do
|
15
|
-
conn.
|
15
|
+
expect(conn).to receive(:synchronous_commit=)
|
16
16
|
ActiveRecord::Base.synchronous_commit = true
|
17
17
|
end
|
18
|
-
end
|
18
|
+
end
|
19
19
|
end
|
data/surus.gemspec
CHANGED
@@ -34,5 +34,6 @@ Gem::Specification.new do |s|
|
|
34
34
|
s.add_development_dependency 'pg'
|
35
35
|
s.add_development_dependency 'pry', '~> 0.9.11'
|
36
36
|
s.add_development_dependency 'factory_girl', '~> 4.2.0'
|
37
|
-
s.add_development_dependency 'faker', '~> 1.
|
37
|
+
s.add_development_dependency 'faker', '~> 1.4.3'
|
38
|
+
s.add_development_dependency 'rake'
|
38
39
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: surus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jack Christensen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -142,14 +142,28 @@ dependencies:
|
|
142
142
|
requirements:
|
143
143
|
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: 1.
|
145
|
+
version: 1.4.3
|
146
146
|
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: 1.
|
152
|
+
version: 1.4.3
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rake
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
153
167
|
description: "Surus accelerates ActiveRecord with PostgreSQL specific types and\n
|
154
168
|
\ functionality. It enables indexed searching of serialized arrays
|
155
169
|
and hashes.\n It also can control PostgreSQL synchronous commit
|
@@ -163,6 +177,7 @@ extra_rdoc_files: []
|
|
163
177
|
files:
|
164
178
|
- ".gitignore"
|
165
179
|
- ".rspec"
|
180
|
+
- ".travis.yml"
|
166
181
|
- CHANGELOG.md
|
167
182
|
- Gemfile
|
168
183
|
- Guardfile
|
@@ -175,6 +190,9 @@ files:
|
|
175
190
|
- bench/hstore_find.rb
|
176
191
|
- bench/json_generation.rb
|
177
192
|
- bench/synchronous_commit.rb
|
193
|
+
- gemfiles/4.0.gemfile
|
194
|
+
- gemfiles/4.1.gemfile
|
195
|
+
- gemfiles/4.2.gemfile
|
178
196
|
- lib/generators/surus/hstore/install_generator.rb
|
179
197
|
- lib/generators/surus/hstore/templates/install_hstore.rb
|
180
198
|
- lib/surus.rb
|
@@ -194,6 +212,7 @@ files:
|
|
194
212
|
- lib/surus/version.rb
|
195
213
|
- spec/array/scope_spec.rb
|
196
214
|
- spec/database.yml
|
215
|
+
- spec/database.yml.travis
|
197
216
|
- spec/database_structure.sql
|
198
217
|
- spec/factories.rb
|
199
218
|
- spec/hstore/scope_spec.rb
|
@@ -224,13 +243,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
224
243
|
version: '0'
|
225
244
|
requirements: []
|
226
245
|
rubyforge_project: ''
|
227
|
-
rubygems_version: 2.
|
246
|
+
rubygems_version: 2.4.5
|
228
247
|
signing_key:
|
229
248
|
specification_version: 4
|
230
249
|
summary: PostgreSQL Acceleration for ActiveRecord
|
231
250
|
test_files:
|
232
251
|
- spec/array/scope_spec.rb
|
233
252
|
- spec/database.yml
|
253
|
+
- spec/database.yml.travis
|
234
254
|
- spec/database_structure.sql
|
235
255
|
- spec/factories.rb
|
236
256
|
- spec/hstore/scope_spec.rb
|