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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa3f38c97bf8c5fee8d2696496b167b7ea312367
4
- data.tar.gz: 6ca9294640f87778851e17d87676273e4f183472
3
+ metadata.gz: bc893ccb84bdea17562e885649be535589247449
4
+ data.tar.gz: b626abd923dbcde6e9a44f0a6c6e5f608d9e7f85
5
5
  SHA512:
6
- metadata.gz: 0fa86cf2f8def862e78f0fb019cd90eb085495c4da0247a6b3fc97a8514b6277e1ec7252960ecb8a32cd6c242f520dde6406cbdb4e171ead83bff92b484fa3cd
7
- data.tar.gz: ee7cf2411e9d881f2cd5c70fcc6a6bfa93e11b8c5197b9d73cff27a4823ad63991fdfe3a683c50e2bccc226c73f9fafcf43bf54c31a2b9f60d69881c38b5a160
6
+ metadata.gz: 76b2b7986d9c44f006396a445ec188973a44ffc5c631a48a7b089ce3585428ad9565e29cc42d6c1153d0aaf662c0d1039abc218d0121d37b3fd7357a9fe166b8
7
+ data.tar.gz: 42e2ca1a05a2e57233e80c4f4f90909095ec7b64b8858bc40b0bf2448d951391765521d618146f115b5bb61ac648a77568ec7d25196950f3316bc30bd7b74683
data/Gemfile CHANGED
@@ -4,4 +4,4 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem "codeclimate-test-reporter", group: :test, require: nil
7
- gem "simplecov", group: :test, require: nil
7
+ gem "simplecov", group: :test, require: nil
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, @version_name).run!(contents, filename)
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(element: element, type: type)
46
+ processors.push(method_or_block: name || block, type: type)
57
47
  else
58
- processors.push(element: element, type: type, opts: opts)
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
- element = Uploader::Element.new(@current_version, name || block)
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
@@ -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, version = nil)
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 ||= @source.read
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(version: @version, data: {model: @model, attached_as: @attached_as})
105
+ @uploader ||= @uploader_klass.new(data: { model: @model, attached_as: @attached_as })
101
106
  end
102
107
  end
103
108
  end
@@ -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
- a = self
32
- b = @klass
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
- @klass.define_singleton_method "attach_file" do |attach_as, uploader_klass|
35
- a.attach_file_hook(self, attach_as, uploader_klass)
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
- versions = uploader_klass.versions || []
38
- b.attached_files[attach_as] ||= []
39
- b.attached_files[attach_as] += versions
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
- b.class_eval do
42
- define_method(attach_as) do |version = nil|
43
- instance_variable_get("@__uploader_#{version}_#{attach_as}") || begin
44
- new_file = ::Saviour::File.new(uploader_klass, self, attach_as, version)
45
- a.file_instantiator_hook(self, new_file, attach_as, version)
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
- instance_variable_set("@__uploader_#{version}_#{attach_as}", new_file)
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
- class << @klass
64
- def attach_validation(attach_as, method_name = nil, &block)
65
- self.__saviour_validations ||= Hash.new { [] }
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
@@ -8,50 +8,35 @@ module Saviour
8
8
  end
9
9
 
10
10
  def delete!
11
- attached_files.each do |column, versions|
12
- (versions + [nil]).each { |version| @model.send(column, version).delete if @model.send(column, version).exists? }
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, versions|
17
+ attached_files.each do |column|
18
18
  base_file_changed = @model.send(column).changed?
19
- original_content = @model.send(column).source_data if base_file_changed
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, version)
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(name) if persistence_layer
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, version).write
50
- persistence_layer.write(name, new_path) if persistence_layer
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, versions|
19
- (versions + [nil]).each { |version| instance_variable_set("@__uploader_#{version}_#{attach_as}", nil) }
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
@@ -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.get(@bucket)
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, @filename = value, filename
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, version_name)
9
- @uploader, @version_name = uploader, version_name
8
+ def initialize(uploader)
9
+ @uploader = uploader
10
10
  end
11
11
 
12
12
  def matching_processors
13
- @uploader.class.processors.select { |processor| !processor[:element].versioned? || processor[:element].version == @version_name }
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 run_element(element, opts, data)
21
- if element.block?
22
- @uploader.instance_exec(data, filename, &element.method_or_block)
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(element.method_or_block, data, filename)
25
+ @uploader.send(method_or_block, data, filename)
26
26
  else
27
- @uploader.send(element.method_or_block, data, filename, opts)
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
- element = processor[:element]
46
+ method_or_block = processor[:method_or_block]
47
47
  opts = processor[:opts]
48
48
 
49
49
  if processor[:type] == :memory
50
- result = run_element(element, opts, contents)
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 = run_element(element, opts, file)
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]