saviour 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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]