saviour 0.3.1 → 0.4.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/Gemfile +1 -1
- data/README.md +2 -2
- data/lib/saviour.rb +0 -4
- data/lib/saviour/base_uploader.rb +4 -25
- data/lib/saviour/config.rb +6 -1
- data/lib/saviour/file.rb +9 -4
- data/lib/saviour/integrator.rb +30 -36
- data/lib/saviour/life_cycle.rb +10 -25
- data/lib/saviour/model.rb +2 -2
- data/lib/saviour/s3_storage.rb +6 -4
- data/lib/saviour/string_source.rb +8 -3
- data/lib/saviour/uploader/processors_runner.rb +11 -11
- data/lib/saviour/uploader/store_dir_extractor.rb +4 -20
- data/lib/saviour/url_source.rb +14 -6
- data/lib/saviour/validator.rb +8 -10
- data/lib/saviour/version.rb +1 -1
- data/saviour.gemspec +2 -2
- data/spec/feature/allow_overriding_attached_as_method.rb +33 -0
- data/spec/feature/follow_file_spec.rb +53 -0
- data/spec/feature/rewind_source_before_read.rb +21 -0
- data/spec/feature/uploader_declaration.rb +38 -0
- data/spec/feature/validations_spec.rb +0 -25
- data/spec/models/base_uploader_spec.rb +10 -173
- data/spec/models/config_spec.rb +20 -0
- data/spec/models/file_spec.rb +11 -7
- data/spec/models/model_spec.rb +4 -84
- data/spec/models/url_source_spec.rb +0 -7
- metadata +26 -26
- data/lib/saviour/attribute_name_calculator.rb +0 -15
- data/lib/saviour/uploader/element.rb +0 -19
- data/spec/feature/versions_spec.rb +0 -185
- data/spec/models/attribute_name_calculator_spec.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc893ccb84bdea17562e885649be535589247449
|
4
|
+
data.tar.gz: b626abd923dbcde6e9a44f0a6c6e5f608d9e7f85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76b2b7986d9c44f006396a445ec188973a44ffc5c631a48a7b089ce3585428ad9565e29cc42d6c1153d0aaf662c0d1039abc218d0121d37b3fd7357a9fe166b8
|
7
|
+
data.tar.gz: 42e2ca1a05a2e57233e80c4f4f90909095ec7b64b8858bc40b0bf2448d951391765521d618146f115b5bb61ac648a77568ec7d25196950f3316bc30bd7b74683
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -270,12 +270,12 @@ to verify existence for every write.
|
|
270
270
|
|
271
271
|
## Source abstraction
|
272
272
|
|
273
|
-
As mentioned before, you can use `File#assign` with any object that responds to `read`. This is already the case for `::File`,
|
273
|
+
As mentioned before, you can use `File#assign` with any io like object that responds to `read` and `rewind`. This is already the case for `::File`,
|
274
274
|
`Tempfile` or `IO`. Since a file requires also a filename, however, in those cases a random filename will be assigned
|
275
275
|
(you can always set the filename using a processor later on).
|
276
276
|
|
277
277
|
Additionally, if the object responds to `#original_filename` then that will be used as a filename instead of generating
|
278
|
-
a random one.
|
278
|
+
a random one, or, if the object responds to `#path` then `File.basename(path)` will be used as a name.
|
279
279
|
|
280
280
|
You can create your own classes implementing this API to extend functionality. This library includes two of them: StringSource
|
281
281
|
and UrlSource.
|
data/lib/saviour.rb
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'fog/aws'
|
3
|
-
|
4
1
|
require 'saviour/version'
|
5
2
|
require 'saviour/base_uploader'
|
6
3
|
require 'saviour/file'
|
@@ -11,7 +8,6 @@ require 'saviour/string_source'
|
|
11
8
|
require 'saviour/url_source'
|
12
9
|
require 'saviour/model'
|
13
10
|
require 'saviour/integrator'
|
14
|
-
require 'saviour/attribute_name_calculator'
|
15
11
|
require 'saviour/source_filename_extractor'
|
16
12
|
require 'saviour/life_cycle'
|
17
13
|
require 'saviour/persistence_layer'
|
@@ -1,13 +1,9 @@
|
|
1
|
-
require_relative 'uploader/element'
|
2
1
|
require_relative 'uploader/store_dir_extractor'
|
3
2
|
require_relative 'uploader/processors_runner'
|
4
3
|
|
5
4
|
module Saviour
|
6
5
|
class BaseUploader
|
7
|
-
attr_reader :version_name
|
8
|
-
|
9
6
|
def initialize(opts = {})
|
10
|
-
@version_name = opts[:version]
|
11
7
|
@data = opts.fetch(:data, {})
|
12
8
|
end
|
13
9
|
|
@@ -28,7 +24,7 @@ module Saviour
|
|
28
24
|
raise RuntimeError, "Please use `store_dir` before trying to write" unless store_dir
|
29
25
|
|
30
26
|
if Config.processing_enabled
|
31
|
-
contents, filename = Uploader::ProcessorsRunner.new(self
|
27
|
+
contents, filename = Uploader::ProcessorsRunner.new(self).run!(contents, filename)
|
32
28
|
end
|
33
29
|
|
34
30
|
path = ::File.join(store_dir, filename)
|
@@ -45,17 +41,11 @@ module Saviour
|
|
45
41
|
@processors ||= []
|
46
42
|
end
|
47
43
|
|
48
|
-
def versions
|
49
|
-
@versions ||= []
|
50
|
-
end
|
51
|
-
|
52
44
|
def process(name = nil, opts = {}, type = :memory, &block)
|
53
|
-
element = Uploader::Element.new(@current_version, name || block)
|
54
|
-
|
55
45
|
if block_given?
|
56
|
-
processors.push(
|
46
|
+
processors.push(method_or_block: name || block, type: type)
|
57
47
|
else
|
58
|
-
processors.push(
|
48
|
+
processors.push(method_or_block: name || block, type: type, opts: opts)
|
59
49
|
end
|
60
50
|
end
|
61
51
|
|
@@ -65,18 +55,7 @@ module Saviour
|
|
65
55
|
|
66
56
|
|
67
57
|
def store_dir(name = nil, &block)
|
68
|
-
|
69
|
-
store_dirs.push(element)
|
70
|
-
end
|
71
|
-
|
72
|
-
def version(name, &block)
|
73
|
-
versions.push(name)
|
74
|
-
|
75
|
-
if block
|
76
|
-
@current_version = name
|
77
|
-
instance_eval(&block)
|
78
|
-
@current_version = nil
|
79
|
-
end
|
58
|
+
store_dirs.push(name || block)
|
80
59
|
end
|
81
60
|
end
|
82
61
|
end
|
data/lib/saviour/config.rb
CHANGED
@@ -19,12 +19,17 @@ module Saviour
|
|
19
19
|
|
20
20
|
def storage
|
21
21
|
Thread.current["Saviour::Config"] ||= {}
|
22
|
-
Thread.current["Saviour::Config"][:storage] || Thread.main["Saviour::Config"][:storage] || NotImplemented.new
|
22
|
+
Thread.current["Saviour::Config"][:storage] || (Thread.main["Saviour::Config"] && Thread.main["Saviour::Config"][:storage]) || NotImplemented.new
|
23
23
|
end
|
24
24
|
|
25
25
|
def storage=(value)
|
26
26
|
Thread.current["Saviour::Config"] ||= {}
|
27
27
|
Thread.current["Saviour::Config"][:storage] = value
|
28
|
+
|
29
|
+
if Thread.main["Saviour::Config"].nil?
|
30
|
+
Thread.main["Saviour::Config"] ||= {}
|
31
|
+
Thread.main["Saviour::Config"][:storage] = value
|
32
|
+
end
|
28
33
|
end
|
29
34
|
end
|
30
35
|
end
|
data/lib/saviour/file.rb
CHANGED
@@ -4,9 +4,8 @@ module Saviour
|
|
4
4
|
class File
|
5
5
|
attr_reader :persisted_path
|
6
6
|
|
7
|
-
def initialize(uploader_klass, model, attached_as
|
7
|
+
def initialize(uploader_klass, model, attached_as)
|
8
8
|
@uploader_klass, @model, @attached_as = uploader_klass, model, attached_as
|
9
|
-
@version = version
|
10
9
|
@source_was = @source = nil
|
11
10
|
end
|
12
11
|
|
@@ -35,6 +34,9 @@ module Saviour
|
|
35
34
|
def assign(object)
|
36
35
|
raise(RuntimeError, "must respond to `read`") if object && !object.respond_to?(:read)
|
37
36
|
|
37
|
+
followers = @model.class.attached_followers_per_leader[@attached_as]
|
38
|
+
followers.each { |x| @model.send(x).assign(object) unless @model.send(x).changed? } if followers
|
39
|
+
|
38
40
|
@source_data = nil
|
39
41
|
@source = object
|
40
42
|
@persisted_path = nil if object
|
@@ -86,7 +88,10 @@ module Saviour
|
|
86
88
|
end
|
87
89
|
|
88
90
|
def source_data
|
89
|
-
@source_data ||=
|
91
|
+
@source_data ||= begin
|
92
|
+
@source.rewind
|
93
|
+
@source.read
|
94
|
+
end
|
90
95
|
end
|
91
96
|
|
92
97
|
def blank?
|
@@ -97,7 +102,7 @@ module Saviour
|
|
97
102
|
private
|
98
103
|
|
99
104
|
def uploader
|
100
|
-
@uploader ||= @uploader_klass.new(
|
105
|
+
@uploader ||= @uploader_klass.new(data: { model: @model, attached_as: @attached_as })
|
101
106
|
end
|
102
107
|
end
|
103
108
|
end
|
data/lib/saviour/integrator.rb
CHANGED
@@ -5,46 +5,40 @@ module Saviour
|
|
5
5
|
@persistence_klass = persistence_klass
|
6
6
|
end
|
7
7
|
|
8
|
-
def file_instantiator_hook(model, file_instance, attach_as, version)
|
9
|
-
layer = @persistence_klass.new(model)
|
10
|
-
file_instance.set_path!(layer.read(Saviour::AttributeNameCalculator.new(attach_as, version).name)) if layer.persisted?
|
11
|
-
end
|
12
|
-
|
13
|
-
def attach_file_hook(klass, attach_as, uploader_klass)
|
14
|
-
versions = uploader_klass.versions || []
|
15
|
-
|
16
|
-
([nil] + versions).each do |version|
|
17
|
-
column_name = Saviour::AttributeNameCalculator.new(attach_as, version).name
|
18
|
-
|
19
|
-
if klass.table_exists? && !klass.column_names.include?(column_name.to_s)
|
20
|
-
raise RuntimeError, "#{klass} must have a database string column named '#{column_name}'"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
8
|
def setup!
|
26
9
|
raise "You cannot include Saviour twice in the same class" if @klass.respond_to?(:attached_files)
|
27
10
|
|
28
11
|
@klass.class_attribute :attached_files
|
29
|
-
@klass.attached_files =
|
12
|
+
@klass.attached_files = []
|
13
|
+
@klass.class_attribute :attached_followers_per_leader
|
14
|
+
@klass.attached_followers_per_leader = {}
|
15
|
+
|
16
|
+
klass = @klass
|
17
|
+
persistence_klass = @persistence_klass
|
30
18
|
|
31
|
-
|
32
|
-
|
19
|
+
@klass.define_singleton_method "attach_file" do |attach_as, *maybe_uploader_klass, **opts, &block|
|
20
|
+
klass.attached_files.push(attach_as)
|
21
|
+
uploader_klass = maybe_uploader_klass[0]
|
33
22
|
|
34
|
-
|
35
|
-
|
23
|
+
if opts[:follow]
|
24
|
+
klass.attached_followers_per_leader[opts[:follow]] ||= []
|
25
|
+
klass.attached_followers_per_leader[opts[:follow]].push(attach_as)
|
26
|
+
end
|
36
27
|
|
37
|
-
|
38
|
-
|
39
|
-
|
28
|
+
if uploader_klass.nil? && block.nil?
|
29
|
+
raise ArgumentError, "you must provide either an UploaderClass or a block to define it."
|
30
|
+
end
|
40
31
|
|
41
|
-
|
42
|
-
define_method(attach_as) do
|
43
|
-
instance_variable_get("@__uploader_#{
|
44
|
-
|
45
|
-
|
32
|
+
mod = Module.new do
|
33
|
+
define_method(attach_as) do
|
34
|
+
instance_variable_get("@__uploader_#{attach_as}") || begin
|
35
|
+
uploader_klass = Class.new(Saviour::BaseUploader, &block) if block
|
36
|
+
new_file = ::Saviour::File.new(uploader_klass, self, attach_as)
|
46
37
|
|
47
|
-
|
38
|
+
layer = persistence_klass.new(self)
|
39
|
+
new_file.set_path!(layer.read(attach_as)) if layer.persisted?
|
40
|
+
|
41
|
+
instance_variable_set("@__uploader_#{attach_as}", new_file)
|
48
42
|
end
|
49
43
|
end
|
50
44
|
|
@@ -56,15 +50,15 @@ module Saviour
|
|
56
50
|
send(attach_as).changed?
|
57
51
|
end
|
58
52
|
end
|
53
|
+
|
54
|
+
klass.include mod
|
59
55
|
end
|
60
56
|
|
61
57
|
@klass.class_attribute :__saviour_validations
|
62
58
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
self.__saviour_validations[attach_as] += [method_name || block]
|
67
|
-
end
|
59
|
+
@klass.define_singleton_method("attach_validation") do |attach_as, method_name = nil, &block|
|
60
|
+
klass.__saviour_validations ||= Hash.new { [] }
|
61
|
+
klass.__saviour_validations[attach_as] += [method_name || block]
|
68
62
|
end
|
69
63
|
end
|
70
64
|
end
|
data/lib/saviour/life_cycle.rb
CHANGED
@@ -8,50 +8,35 @@ module Saviour
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def delete!
|
11
|
-
attached_files.each do |column
|
12
|
-
|
11
|
+
attached_files.each do |column|
|
12
|
+
@model.send(column).delete if @model.send(column).exists?
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
def save!
|
17
|
-
attached_files.each do |column
|
17
|
+
attached_files.each do |column|
|
18
18
|
base_file_changed = @model.send(column).changed?
|
19
|
-
|
20
|
-
|
21
|
-
versions.each do |version|
|
22
|
-
if @model.send(column, version).changed?
|
23
|
-
upload_file(column, version)
|
24
|
-
elsif base_file_changed
|
25
|
-
@model.send(column, version).assign(StringSource.new(original_content, default_version_filename(column, version)))
|
26
|
-
upload_file(column, version)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
upload_file(column, nil) if base_file_changed
|
19
|
+
|
20
|
+
upload_file(column) if base_file_changed
|
31
21
|
end
|
32
22
|
end
|
33
23
|
|
34
24
|
|
35
25
|
private
|
36
26
|
|
37
|
-
def default_version_filename(column, version)
|
38
|
-
filename = @model.send(column).filename_to_be_assigned
|
39
|
-
"#{::File.basename(filename, ".*")}_#{version}#{::File.extname(filename)}"
|
40
|
-
end
|
41
27
|
|
42
|
-
def upload_file(column
|
43
|
-
name = AttributeNameCalculator.new(column, version).name
|
28
|
+
def upload_file(column)
|
44
29
|
persistence_layer = @persistence_klass.new(@model) if @persistence_klass
|
45
|
-
current_path = persistence_layer.read(
|
30
|
+
current_path = persistence_layer.read(column) if persistence_layer
|
46
31
|
|
47
32
|
Config.storage.delete(current_path) if current_path
|
48
33
|
|
49
|
-
new_path = @model.send(column
|
50
|
-
persistence_layer.write(
|
34
|
+
new_path = @model.send(column).write
|
35
|
+
persistence_layer.write(column, new_path) if persistence_layer
|
51
36
|
end
|
52
37
|
|
53
38
|
def attached_files
|
54
|
-
@model.class.attached_files
|
39
|
+
@model.class.attached_files
|
55
40
|
end
|
56
41
|
end
|
57
42
|
end
|
data/lib/saviour/model.rb
CHANGED
@@ -15,8 +15,8 @@ module Saviour
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def reload
|
18
|
-
self.class.attached_files.each do |attach_as
|
19
|
-
|
18
|
+
self.class.attached_files.each do |attach_as|
|
19
|
+
instance_variable_set("@__uploader_#{attach_as}", nil)
|
20
20
|
end
|
21
21
|
super
|
22
22
|
end
|
data/lib/saviour/s3_storage.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'fog/aws'
|
2
|
+
|
1
3
|
module Saviour
|
2
4
|
class S3Storage
|
3
5
|
def initialize(conf = {})
|
@@ -6,8 +8,8 @@ module Saviour
|
|
6
8
|
@conf = conf
|
7
9
|
@overwrite_protection = conf.delete(:overwrite_protection) { true }
|
8
10
|
@create_options = conf.delete(:create_options) { {} }
|
9
|
-
conf.fetch(:aws_access_key_id) { raise ArgumentError.new("aws_access_key_id is required")}
|
10
|
-
conf.fetch(:aws_secret_access_key) { raise ArgumentError.new("aws_secret_access_key is required")}
|
11
|
+
conf.fetch(:aws_access_key_id) { raise ArgumentError.new("aws_access_key_id is required") }
|
12
|
+
conf.fetch(:aws_secret_access_key) { raise ArgumentError.new("aws_secret_access_key is required") }
|
11
13
|
end
|
12
14
|
|
13
15
|
def write(contents, path)
|
@@ -66,11 +68,11 @@ module Saviour
|
|
66
68
|
end
|
67
69
|
|
68
70
|
def directory
|
69
|
-
@directory ||= connection.directories.
|
71
|
+
@directory ||= connection.directories.new(key: @bucket)
|
70
72
|
end
|
71
73
|
|
72
74
|
def connection
|
73
|
-
@connection ||= Fog::Storage.new({provider: 'AWS'}.merge(@conf))
|
75
|
+
@connection ||= Fog::Storage.new({ provider: 'AWS' }.merge(@conf))
|
74
76
|
end
|
75
77
|
end
|
76
78
|
end
|
@@ -1,11 +1,16 @@
|
|
1
1
|
module Saviour
|
2
2
|
class StringSource
|
3
3
|
def initialize(value, filename = nil)
|
4
|
-
@value
|
4
|
+
@value = StringIO.new(value)
|
5
|
+
@filename = filename
|
5
6
|
end
|
6
7
|
|
7
|
-
def read
|
8
|
-
@value
|
8
|
+
def read(*args)
|
9
|
+
@value.read(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def rewind
|
13
|
+
@value.rewind
|
9
14
|
end
|
10
15
|
|
11
16
|
def original_filename
|
@@ -5,26 +5,26 @@ module Saviour
|
|
5
5
|
attr_accessor :contents
|
6
6
|
attr_accessor :filename
|
7
7
|
|
8
|
-
def initialize(uploader
|
9
|
-
@uploader
|
8
|
+
def initialize(uploader)
|
9
|
+
@uploader = uploader
|
10
10
|
end
|
11
11
|
|
12
12
|
def matching_processors
|
13
|
-
@uploader.class.processors
|
13
|
+
@uploader.class.processors
|
14
14
|
end
|
15
15
|
|
16
16
|
def file
|
17
17
|
@file ||= Tempfile.new([SecureRandom.hex, ::File.extname(filename)]).tap { |x| x.binmode }
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
if
|
22
|
-
@uploader.instance_exec(data, filename, &
|
20
|
+
def run_method_or_block(method_or_block, opts, data)
|
21
|
+
if method_or_block.respond_to?(:call)
|
22
|
+
@uploader.instance_exec(data, filename, &method_or_block)
|
23
23
|
else
|
24
24
|
if opts.empty?
|
25
|
-
@uploader.send(
|
25
|
+
@uploader.send(method_or_block, data, filename)
|
26
26
|
else
|
27
|
-
@uploader.send(
|
27
|
+
@uploader.send(method_or_block, data, filename, opts)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -43,11 +43,11 @@ module Saviour
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def run_processor(processor)
|
46
|
-
|
46
|
+
method_or_block = processor[:method_or_block]
|
47
47
|
opts = processor[:opts]
|
48
48
|
|
49
49
|
if processor[:type] == :memory
|
50
|
-
result =
|
50
|
+
result = run_method_or_block(method_or_block, opts, contents)
|
51
51
|
|
52
52
|
self.contents = result[0]
|
53
53
|
self.filename = result[1]
|
@@ -55,7 +55,7 @@ module Saviour
|
|
55
55
|
else
|
56
56
|
file.rewind
|
57
57
|
|
58
|
-
result =
|
58
|
+
result = run_method_or_block(method_or_block, opts, file)
|
59
59
|
|
60
60
|
self.file = result[0]
|
61
61
|
self.filename = result[1]
|