refile-postgres 1.1.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|