refile-postgres 1.1.4 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/refile/postgres/initializer/templates/refile.rb +2 -1
- data/lib/refile/postgres/backend/reader.rb +32 -18
- data/lib/refile/postgres/backend.rb +48 -63
- data/lib/refile/postgres/smart_transaction.rb +24 -4
- data/lib/refile/postgres/version.rb +1 -1
- data/refile-postgres.gemspec +2 -2
- data/spec/refile/postgres/backend_spec.rb +23 -33
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcbf870ad7c94880999b64a72c48264070344114
|
4
|
+
data.tar.gz: e9015db34ee5fcc3e39963894d9b1fe2c3bc4827
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8164bf9a9cd8dc25d88c809490ba28ed3dd9b1b997da8a892a9a30022c39cfe0dba14ecbc574f2744392b5950a0f9b095308d10cd4e163e102f62d0c855522a7
|
7
|
+
data.tar.gz: 0a84dff267e690011f4a219042b6e674409fa1249555671ca248e8dd52c8f073afc0bdd2e92afc1111d711266cf828619386316088cf4188550b35ec1fc2a7dc
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "refile"
|
2
2
|
Refile.configure do |config|
|
3
|
-
|
3
|
+
connection = lambda { |&blk| ActiveRecord::Base.connection_pool.with_connection { |con| blk.call(con.raw_connection) } }
|
4
|
+
config.store = Refile::Postgres::Backend.new(connection)
|
4
5
|
end
|
@@ -4,27 +4,31 @@ module Refile
|
|
4
4
|
class Reader
|
5
5
|
include SmartTransaction
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
7
|
+
def initialize(connection_or_proc, oid)
|
8
|
+
@connection_or_proc = connection_or_proc
|
9
9
|
@oid = oid.to_s.to_i
|
10
10
|
@closed = false
|
11
11
|
@pos = 0
|
12
12
|
end
|
13
13
|
|
14
|
-
attr_reader :
|
14
|
+
attr_reader :oid, :pos
|
15
15
|
|
16
16
|
def read(length = nil, buffer = nil)
|
17
17
|
result = if length
|
18
18
|
raise "closed" if @closed
|
19
|
-
|
20
|
-
connection
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
with_connection do |connection|
|
20
|
+
smart_transaction(connection) do |descriptor|
|
21
|
+
connection.lo_lseek(descriptor, @pos, PG::SEEK_SET)
|
22
|
+
data = connection.lo_read(descriptor, length)
|
23
|
+
@pos = connection.lo_tell(descriptor)
|
24
|
+
data
|
25
|
+
end
|
24
26
|
end
|
25
27
|
else
|
26
|
-
|
27
|
-
connection
|
28
|
+
with_connection do |connection|
|
29
|
+
smart_transaction(connection) do |descriptor|
|
30
|
+
connection.lo_read(descriptor, size)
|
31
|
+
end
|
28
32
|
end
|
29
33
|
end
|
30
34
|
buffer.replace(result) if buffer and result
|
@@ -32,24 +36,34 @@ module Refile
|
|
32
36
|
end
|
33
37
|
|
34
38
|
def eof?
|
35
|
-
|
36
|
-
|
39
|
+
with_connection do |connection|
|
40
|
+
smart_transaction(connection) do |descriptor|
|
41
|
+
@pos == size
|
42
|
+
end
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
40
46
|
def size
|
41
|
-
@size ||=
|
42
|
-
current_position = connection.lo_tell(descriptor)
|
43
|
-
end_position = connection.lo_lseek(descriptor, 0, PG::SEEK_END)
|
44
|
-
connection.lo_lseek(descriptor, current_position, PG::SEEK_SET)
|
45
|
-
end_position
|
46
|
-
end
|
47
|
+
@size ||= fetch_size
|
47
48
|
end
|
48
49
|
|
49
50
|
def close
|
50
51
|
@closed = true
|
51
52
|
end
|
52
53
|
|
54
|
+
private
|
55
|
+
|
56
|
+
def fetch_size
|
57
|
+
with_connection do |connection|
|
58
|
+
smart_transaction(connection) do |descriptor|
|
59
|
+
current_position = connection.lo_tell(descriptor)
|
60
|
+
end_position = connection.lo_lseek(descriptor, 0, PG::SEEK_END)
|
61
|
+
connection.lo_lseek(descriptor, current_position, PG::SEEK_SET)
|
62
|
+
end_position
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
53
67
|
end
|
54
68
|
end
|
55
69
|
end
|
@@ -8,7 +8,6 @@ module Refile
|
|
8
8
|
DEFAULT_NAMESPACE = "default"
|
9
9
|
PG_LARGE_OBJECT_METADATA_TABLE = "pg_largeobject_metadata"
|
10
10
|
READ_CHUNK_SIZE = 3000
|
11
|
-
INIT_CONNECTION_ARG_ERROR_MSG = "When initializing new Refile::Postgres::Backend first argument should be an instance of PG::Connection or a lambda/proc that returns it. When using ActiveRecord it is available as ActiveRecord::Base.connection.raw_connection"
|
12
11
|
|
13
12
|
def initialize(connection_or_proc, max_size: nil, namespace: DEFAULT_NAMESPACE, registry_table: DEFAULT_REGISTRY_TABLE)
|
14
13
|
@connection_or_proc = connection_or_proc
|
@@ -22,12 +21,11 @@ module Refile
|
|
22
21
|
|
23
22
|
def registry_table
|
24
23
|
unless @registry_table_validated
|
25
|
-
connection
|
26
|
-
SELECT
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
raise RegistryTableDoesNotExistError.new(%{Please create a table "#{@registry_table}" where backend could store list of attachments})
|
24
|
+
with_connection do |connection|
|
25
|
+
connection.exec_params("SELECT * FROM pg_catalog.pg_tables WHERE tablename = $1::varchar;", [@registry_table]) do |result|
|
26
|
+
if result.count != 1
|
27
|
+
raise RegistryTableDoesNotExistError.new(%{Please create a table "#{@registry_table}" where backend could store list of attachments})
|
28
|
+
end
|
31
29
|
end
|
32
30
|
end
|
33
31
|
@registry_table_validated = true
|
@@ -35,37 +33,31 @@ module Refile
|
|
35
33
|
@registry_table
|
36
34
|
end
|
37
35
|
|
38
|
-
def connection
|
39
|
-
if has_active_connection?
|
40
|
-
@connection
|
41
|
-
else
|
42
|
-
obtain_new_connection
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
36
|
verify_uploadable def upload(uploadable)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
uploadable.
|
55
|
-
|
37
|
+
with_connection do |connection|
|
38
|
+
oid = connection.lo_creat
|
39
|
+
ensure_in_transaction(connection) do
|
40
|
+
begin
|
41
|
+
handle = connection.lo_open(oid, PG::INV_WRITE)
|
42
|
+
connection.lo_truncate(handle, 0)
|
43
|
+
buffer = "" # reuse the same buffer
|
44
|
+
until uploadable.eof?
|
45
|
+
uploadable.read(READ_CHUNK_SIZE, buffer)
|
46
|
+
connection.lo_write(handle, buffer)
|
47
|
+
end
|
48
|
+
uploadable.close
|
49
|
+
connection.exec_params("INSERT INTO #{registry_table} VALUES ($1::integer, $2::varchar);", [oid, namespace])
|
50
|
+
Refile::File.new(self, oid.to_s)
|
51
|
+
ensure
|
52
|
+
connection.lo_close(handle)
|
56
53
|
end
|
57
|
-
uploadable.close
|
58
|
-
connection.exec_params("INSERT INTO #{registry_table} VALUES ($1::integer, $2::varchar);", [oid, namespace])
|
59
|
-
Refile::File.new(self, oid.to_s)
|
60
|
-
ensure
|
61
|
-
connection.lo_close(handle)
|
62
54
|
end
|
63
55
|
end
|
64
56
|
end
|
65
57
|
|
66
58
|
verify_id def open(id)
|
67
59
|
if exists?(id)
|
68
|
-
Reader.new(
|
60
|
+
Reader.new(@connection_or_proc, id)
|
69
61
|
else
|
70
62
|
raise ArgumentError.new("No such attachment with ID: #{id}")
|
71
63
|
end
|
@@ -84,14 +76,16 @@ module Refile
|
|
84
76
|
end
|
85
77
|
|
86
78
|
verify_id def exists?(id)
|
87
|
-
connection
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
79
|
+
with_connection do |connection|
|
80
|
+
connection.exec_params(%{
|
81
|
+
SELECT count(*) FROM #{registry_table}
|
82
|
+
INNER JOIN #{PG_LARGE_OBJECT_METADATA_TABLE}
|
83
|
+
ON #{registry_table}.id = #{PG_LARGE_OBJECT_METADATA_TABLE}.oid
|
84
|
+
WHERE #{registry_table}.namespace = $1::varchar
|
85
|
+
AND #{registry_table}.id = $2::integer;
|
86
|
+
}, [namespace, id.to_s.to_i]) do |result|
|
87
|
+
result[0]["count"].to_i > 0
|
88
|
+
end
|
95
89
|
end
|
96
90
|
end
|
97
91
|
|
@@ -105,9 +99,11 @@ module Refile
|
|
105
99
|
|
106
100
|
verify_id def delete(id)
|
107
101
|
if exists?(id)
|
108
|
-
|
109
|
-
connection
|
110
|
-
|
102
|
+
with_connection do |connection|
|
103
|
+
ensure_in_transaction(connection) do
|
104
|
+
connection.lo_unlink(id.to_s.to_i)
|
105
|
+
connection.exec_params("DELETE FROM #{registry_table} WHERE id = $1::integer;", [id])
|
106
|
+
end
|
111
107
|
end
|
112
108
|
end
|
113
109
|
end
|
@@ -115,33 +111,22 @@ module Refile
|
|
115
111
|
def clear!(confirm = nil)
|
116
112
|
raise Refile::Confirm unless confirm == :confirm
|
117
113
|
registry_table
|
118
|
-
|
119
|
-
connection
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
114
|
+
with_connection do |connection|
|
115
|
+
ensure_in_transaction(connection) do
|
116
|
+
connection.exec_params(%{
|
117
|
+
SELECT * FROM #{registry_table}
|
118
|
+
INNER JOIN #{PG_LARGE_OBJECT_METADATA_TABLE} ON #{registry_table}.id = #{PG_LARGE_OBJECT_METADATA_TABLE}.oid
|
119
|
+
WHERE #{registry_table}.namespace = $1::varchar;
|
120
|
+
}, [namespace]) do |result|
|
121
|
+
result.each_row do |row|
|
122
|
+
connection.lo_unlink(row[0].to_s.to_i)
|
123
|
+
end
|
126
124
|
end
|
125
|
+
connection.exec_params("DELETE FROM #{registry_table} WHERE namespace = $1::varchar;", [namespace])
|
127
126
|
end
|
128
|
-
connection.exec_params("DELETE FROM #{registry_table} WHERE namespace = $1::varchar;", [namespace])
|
129
127
|
end
|
130
128
|
end
|
131
129
|
|
132
|
-
private
|
133
|
-
|
134
|
-
def has_active_connection?
|
135
|
-
@connection && !@connection.finished?
|
136
|
-
end
|
137
|
-
|
138
|
-
def obtain_new_connection
|
139
|
-
candidate = @connection_or_proc.is_a?(Proc) ? @connection_or_proc.call : @connection_or_proc
|
140
|
-
unless candidate.is_a?(PG::Connection)
|
141
|
-
raise ArgumentError.new(INIT_CONNECTION_ARG_ERROR_MSG)
|
142
|
-
end
|
143
|
-
@connection = candidate
|
144
|
-
end
|
145
130
|
end
|
146
131
|
end
|
147
132
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module Refile
|
2
2
|
module Postgres
|
3
3
|
module SmartTransaction
|
4
|
-
|
4
|
+
INIT_CONNECTION_ARG_ERROR_MSG = "When initializing new Refile::Postgres::Backend first argument should be an instance of PG::Connection or a lambda/proc that yields it."
|
5
5
|
PQTRANS_INTRANS = 2 # (idle, within transaction block)
|
6
6
|
|
7
|
-
def smart_transaction
|
7
|
+
def smart_transaction(connection)
|
8
8
|
result = nil
|
9
|
-
ensure_in_transaction do
|
9
|
+
ensure_in_transaction(connection) do
|
10
10
|
begin
|
11
11
|
handle = connection.lo_open(oid)
|
12
12
|
result = yield handle
|
@@ -16,7 +16,7 @@ module Refile
|
|
16
16
|
result
|
17
17
|
end
|
18
18
|
|
19
|
-
def ensure_in_transaction
|
19
|
+
def ensure_in_transaction(connection)
|
20
20
|
if connection.transaction_status == PQTRANS_INTRANS
|
21
21
|
yield
|
22
22
|
else
|
@@ -26,6 +26,26 @@ module Refile
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
def with_connection
|
30
|
+
if @connection_or_proc.is_a?(PG::Connection)
|
31
|
+
yield @connection_or_proc
|
32
|
+
else
|
33
|
+
if @connection_or_proc.is_a?(Proc)
|
34
|
+
block_has_been_executed = false
|
35
|
+
value = nil
|
36
|
+
@connection_or_proc.call do |connection|
|
37
|
+
block_has_been_executed = true
|
38
|
+
raise ArgumentError.new(INIT_CONNECTION_ARG_ERROR_MSG) unless connection.is_a?(PG::Connection)
|
39
|
+
value = yield connection
|
40
|
+
end
|
41
|
+
raise ArgumentError.new(INIT_CONNECTION_ARG_ERROR_MSG) unless block_has_been_executed
|
42
|
+
value
|
43
|
+
else
|
44
|
+
raise ArgumentError.new(INIT_CONNECTION_ARG_ERROR_MSG)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
29
49
|
end
|
30
50
|
end
|
31
51
|
end
|
data/refile-postgres.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency "refile", "~> 0.6.
|
21
|
+
spec.add_dependency "refile", "~> 0.6.2"
|
22
22
|
spec.add_dependency "pg"
|
23
23
|
|
24
24
|
spec.add_development_dependency "bundler"
|
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency "webmock"
|
27
27
|
spec.add_development_dependency "pry"
|
28
28
|
spec.add_development_dependency "pry-stack_explorer"
|
29
|
-
spec.add_development_dependency "rails", "~> 4.2.
|
29
|
+
spec.add_development_dependency "rails", "~> 4.2.5"
|
30
30
|
spec.add_development_dependency "rake"
|
31
31
|
spec.add_development_dependency "codeclimate-test-reporter"
|
32
32
|
end
|
@@ -1,47 +1,37 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
|
4
|
-
let(:
|
3
|
+
describe Refile::Postgres::Backend do
|
4
|
+
let(:connection) { PG.connect(dbname: 'refile_test') }
|
5
5
|
let(:backend) { Refile::Postgres::Backend.new(connection_or_proc, max_size: 100) }
|
6
|
-
it_behaves_like :backend
|
7
6
|
|
8
7
|
context "Connection tests" do
|
9
|
-
|
10
|
-
|
8
|
+
context "when not using procs and providing PG::Connection directly" do
|
9
|
+
let(:connection_or_proc) { connection }
|
10
|
+
it "reuses the same PG::Connection" do
|
11
|
+
expect(backend.with_connection { |c| c.db }).to eq("refile_test")
|
12
|
+
end
|
11
13
|
end
|
12
14
|
|
13
15
|
context "when using proc" do
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
context "when lambda does not yield a block but returns connection" do
|
17
|
+
let(:connection_or_proc) { lambda { connection } }
|
18
|
+
it "raises argument error" do
|
19
|
+
expect {
|
20
|
+
backend.with_connection { |c| c.db }
|
21
|
+
}.to raise_error(ArgumentError, "When initializing new Refile::Postgres::Backend first argument should be an instance of PG::Connection or a lambda/proc that yields it.")
|
22
|
+
end
|
20
23
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
expect(backend.connection.finished?).to be_falsey
|
24
|
+
context "when lambda does yield a PG::Connection" do
|
25
|
+
let(:connection_or_proc) { lambda { |&blk| blk.call(connection) } }
|
26
|
+
it "is usable in queries" do
|
27
|
+
expect(backend.with_connection { |c| c.db }).to eq("refile_test")
|
28
|
+
end
|
27
29
|
end
|
28
30
|
end
|
31
|
+
end
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
it "reuses the same PG::Connection" do
|
36
|
-
expect(backend.connection).to eq(backend.connection)
|
37
|
-
end
|
38
|
-
|
39
|
-
it "continues to use old connection if the old one is closed" do
|
40
|
-
old = backend.connection
|
41
|
-
old.close
|
42
|
-
expect(backend.connection).to eq(old)
|
43
|
-
expect(backend.connection.finished?).to be_truthy
|
44
|
-
end
|
45
|
-
end
|
33
|
+
context "Refile Provided tests" do
|
34
|
+
let(:connection_or_proc) { connection }
|
35
|
+
it_behaves_like :backend
|
46
36
|
end
|
47
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: refile-postgres
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Krists Ozols
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: refile
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.6.
|
19
|
+
version: 0.6.2
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.6.
|
26
|
+
version: 0.6.2
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 4.2.
|
117
|
+
version: 4.2.5
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 4.2.
|
124
|
+
version: 4.2.5
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: rake
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|