saviour 0.3.0 → 0.3.1

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: 8673c1ddde4f8e16189bfd2d54ea7ad239155679
4
- data.tar.gz: 8a97558346780d9105a5b2145fcdc3a038ae3bc6
3
+ metadata.gz: fa3f38c97bf8c5fee8d2696496b167b7ea312367
4
+ data.tar.gz: 6ca9294640f87778851e17d87676273e4f183472
5
5
  SHA512:
6
- metadata.gz: 541052bdedf19b045140c57fa154d9f40007f56bd4fd4cd63895d99a2f577d24e4e10e39e876c597c1bc38fbd462797dcc59173d3b5f36d89aaa06e91c2cf52c
7
- data.tar.gz: beea334842a6e686c7df91b933ed55a70298fb00818a950381c2d015bc138ff90ff7deff9b7d74f0a4c150a8069600800f5afc5ff002d77b92e3e1cb0fde2380
6
+ metadata.gz: 0fa86cf2f8def862e78f0fb019cd90eb085495c4da0247a6b3fc97a8514b6277e1ec7252960ecb8a32cd6c242f520dde6406cbdb4e171ead83bff92b484fa3cd
7
+ data.tar.gz: ee7cf2411e9d881f2cd5c70fcc6a6bfa93e11b8c5197b9d73cff27a4823ad63991fdfe3a683c50e2bccc226c73f9fafcf43bf54c31a2b9f60d69881c38b5a160
data/.travis.yml CHANGED
@@ -5,6 +5,13 @@ rvm:
5
5
  - 2.1.8
6
6
  - 2.2.4
7
7
  - 2.3.0
8
+ - 2.4.0
9
+
10
+ gemfile:
11
+ - gemfiles/4.0.gemfile
12
+ - gemfiles/4.1.gemfile
13
+ - gemfiles/4.2.gemfile
14
+ - gemfiles/5.0.gemfile
8
15
 
9
16
  addons:
10
17
  code_climate:
@@ -12,3 +19,15 @@ addons:
12
19
 
13
20
  after_success:
14
21
  - bundle exec codeclimate-test-reporter
22
+
23
+ matrix:
24
+ exclude:
25
+ # rails 5 requires 2.2+
26
+ - rvm: 2.1.8
27
+ gemfile: gemfiles/5.0.gemfile
28
+
29
+ # Arel support for ruby 2.4 starts in 4.2
30
+ - rvm: 2.4.0
31
+ gemfile: gemfiles/4.0.gemfile
32
+ - rvm: 2.4.0
33
+ gemfile: gemfiles/4.1.gemfile
data/Appraisals ADDED
@@ -0,0 +1,19 @@
1
+ appraise "4.0" do
2
+ gem "activesupport", "~> 4.0.0"
3
+ gem "activerecord", "~> 4.0.0"
4
+ end
5
+
6
+ appraise "4.1" do
7
+ gem "activesupport", "~> 4.1.0"
8
+ gem "activerecord", "~> 4.1.0"
9
+ end
10
+
11
+ appraise "4.2" do
12
+ gem "activesupport", "~> 4.2.0"
13
+ gem "activerecord", "~> 4.2.0"
14
+ end
15
+
16
+ appraise "5.0" do
17
+ gem "activesupport", "~> 5.0.0"
18
+ gem "activerecord", "~> 5.0.0"
19
+ end
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "codeclimate-test-reporter", :group => :test, :require => nil
6
+ gem "activesupport", "~> 4.0.0"
7
+ gem "activerecord", "~> 4.0.0"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "codeclimate-test-reporter", :group => :test, :require => nil
6
+ gem "activesupport", "~> 4.1.0"
7
+ gem "activerecord", "~> 4.1.0"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "codeclimate-test-reporter", :group => :test, :require => nil
6
+ gem "activesupport", "~> 4.2.0"
7
+ gem "activerecord", "~> 4.2.0"
8
+
9
+ gemspec :path => "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "codeclimate-test-reporter", :group => :test, :require => nil
6
+ gem "activesupport", "~> 5.0.0"
7
+ gem "activerecord", "~> 5.0.0"
8
+
9
+ gemspec :path => "../"
data/lib/saviour.rb CHANGED
@@ -1,8 +1,6 @@
1
1
  require 'fileutils'
2
2
  require 'fog/aws'
3
3
 
4
- require 'saviour/utils/class_attribute'
5
-
6
4
  require 'saviour/version'
7
5
  require 'saviour/base_uploader'
8
6
  require 'saviour/file'
@@ -11,12 +9,13 @@ require 'saviour/s3_storage'
11
9
  require 'saviour/config'
12
10
  require 'saviour/string_source'
13
11
  require 'saviour/url_source'
14
- require 'saviour/basic_model'
15
- require 'saviour/base_integrator'
12
+ require 'saviour/model'
13
+ require 'saviour/integrator'
16
14
  require 'saviour/attribute_name_calculator'
17
15
  require 'saviour/source_filename_extractor'
18
16
  require 'saviour/life_cycle'
19
-
17
+ require 'saviour/persistence_layer'
18
+ require 'saviour/validator'
20
19
 
21
20
  module Saviour
22
21
  end
@@ -19,7 +19,7 @@ module Saviour
19
19
  end
20
20
  end
21
21
 
22
- def respond_to?(name, *)
22
+ def respond_to_missing?(name, *)
23
23
  @data.key?(name) || super
24
24
  end
25
25
 
@@ -27,7 +27,9 @@ module Saviour
27
27
  store_dir = Uploader::StoreDirExtractor.new(self).store_dir
28
28
  raise RuntimeError, "Please use `store_dir` before trying to write" unless store_dir
29
29
 
30
- contents, filename = Uploader::ProcessorsRunner.new(self, @version_name).run!(contents, filename) if Config.processing_enabled
30
+ if Config.processing_enabled
31
+ contents, filename = Uploader::ProcessorsRunner.new(self, @version_name).run!(contents, filename)
32
+ end
31
33
 
32
34
  path = ::File.join(store_dir, filename)
33
35
  Config.storage.write(contents, path)
@@ -51,9 +53,9 @@ module Saviour
51
53
  element = Uploader::Element.new(@current_version, name || block)
52
54
 
53
55
  if block_given?
54
- processors.push({element: element, type: type})
56
+ processors.push(element: element, type: type)
55
57
  else
56
- processors.push({element: element, type: type, opts: opts})
58
+ processors.push(element: element, type: type, opts: opts)
57
59
  end
58
60
  end
59
61
 
@@ -1,21 +1,30 @@
1
1
  module Saviour
2
- class BaseIntegrator
3
- def initialize(klass)
2
+ class Integrator
3
+ def initialize(klass, persistence_klass)
4
4
  @klass = klass
5
+ @persistence_klass = persistence_klass
5
6
  end
6
7
 
7
8
  def file_instantiator_hook(model, file_instance, attach_as, version)
8
- # noop
9
+ layer = @persistence_klass.new(model)
10
+ file_instance.set_path!(layer.read(Saviour::AttributeNameCalculator.new(attach_as, version).name)) if layer.persisted?
9
11
  end
10
12
 
11
13
  def attach_file_hook(klass, attach_as, uploader_klass)
12
- # noop
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
13
23
  end
14
24
 
15
25
  def setup!
16
26
  raise "You cannot include Saviour twice in the same class" if @klass.respond_to?(:attached_files)
17
27
 
18
- @klass.send :extend, ClassAttribute
19
28
  @klass.class_attribute :attached_files
20
29
  @klass.attached_files = {}
21
30
 
@@ -48,6 +57,15 @@ module Saviour
48
57
  end
49
58
  end
50
59
  end
60
+
61
+ @klass.class_attribute :__saviour_validations
62
+
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
68
+ end
51
69
  end
52
70
  end
53
71
  end
@@ -63,7 +63,7 @@ module Saviour
63
63
  basedir = ::File.dirname(path)
64
64
  return if basedir == "."
65
65
 
66
- while basedir != "/" && Dir.entries(real_path(basedir)) == [".", ".."]
66
+ while basedir != "/" && Dir.entries(real_path(basedir)) - [".", ".."] ==[]
67
67
  Dir.rmdir(real_path(basedir))
68
68
  basedir = ::File.dirname(basedir)
69
69
  end
@@ -0,0 +1,24 @@
1
+ module Saviour
2
+ NoActiveRecordDetected = Class.new(StandardError)
3
+
4
+ module Model
5
+ def self.included(klass)
6
+ Integrator.new(klass, PersistenceLayer).setup!
7
+
8
+ klass.class_eval do
9
+ raise(NoActiveRecordDetected, "Error: ActiveRecord not detected in #{self}") unless self.ancestors.include?(ActiveRecord::Base)
10
+
11
+ after_destroy { Saviour::LifeCycle.new(self, PersistenceLayer).delete! }
12
+ after_save { Saviour::LifeCycle.new(self, PersistenceLayer).save! }
13
+ validate { Saviour::Validator.new(self).validate! }
14
+ end
15
+ end
16
+
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) }
20
+ end
21
+ super
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ module Saviour
2
+ class PersistenceLayer
3
+ def initialize(model)
4
+ @model = model
5
+ end
6
+
7
+ def read(attr)
8
+ @model.read_attribute(attr)
9
+ end
10
+
11
+ def write(attr, value)
12
+ @model.update_columns(attr => value)
13
+ end
14
+
15
+ def persisted?
16
+ @model.persisted? || @model.destroyed?
17
+ end
18
+ end
19
+ end
@@ -6,7 +6,8 @@ module Saviour
6
6
  @conf = conf
7
7
  @overwrite_protection = conf.delete(:overwrite_protection) { true }
8
8
  @create_options = conf.delete(:create_options) { {} }
9
- assert_directory_exists!
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")}
10
11
  end
11
12
 
12
13
  def write(contents, path)
@@ -60,10 +61,6 @@ module Saviour
60
61
  path.gsub(/\A\/*/, '')
61
62
  end
62
63
 
63
- def assert_directory_exists!
64
- directory || raise(ArgumentError, "The bucket #{@bucket} doesn't exists or misconfigured connection.")
65
- end
66
-
67
64
  def assert_exists(path)
68
65
  raise RuntimeError, "File does not exists: #{path}" unless exists?(path)
69
66
  end
@@ -1,4 +1,5 @@
1
1
  require 'uri'
2
+ require 'net/http'
2
3
 
3
4
  module Saviour
4
5
  class UrlSource
@@ -0,0 +1,50 @@
1
+ module Saviour
2
+ class Validator
3
+ def initialize(model)
4
+ raise "Please provide an object compatible with Saviour." unless model.class.respond_to?(:attached_files)
5
+
6
+ @model = model
7
+ end
8
+
9
+ def validate!
10
+ validations.each do |column, method_or_blocks|
11
+ raise(ArgumentError, "There is no attachment defined as '#{column}'") unless attached_files[column]
12
+ (attached_files[column] + [nil]).each do |version|
13
+ if @model.send(column, version).changed?
14
+ method_or_blocks.each { |method_or_block| run_validation(column, version, method_or_block) }
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def run_validation(column, version, method_or_block)
23
+ data = @model.send(column, version).source_data
24
+ filename = @model.send(column, version).filename_to_be_assigned
25
+ opts = {attached_as: column, version: version}
26
+
27
+ if method_or_block.respond_to?(:call)
28
+ if method_or_block.arity == 2
29
+ @model.instance_exec(data, filename, &method_or_block)
30
+ else
31
+ @model.instance_exec(data, filename, opts, &method_or_block)
32
+ end
33
+ else
34
+ if @model.method(method_or_block).arity == 2
35
+ @model.send(method_or_block, data, filename)
36
+ else
37
+ @model.send(method_or_block, data, filename, opts)
38
+ end
39
+ end
40
+ end
41
+
42
+ def attached_files
43
+ @model.class.attached_files || {}
44
+ end
45
+
46
+ def validations
47
+ @model.class.__saviour_validations || {}
48
+ end
49
+ end
50
+ end
@@ -1,3 +1,3 @@
1
1
  module Saviour
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
data/saviour.gemspec CHANGED
@@ -17,7 +17,12 @@ Gem::Specification.new do |spec|
17
17
 
18
18
  spec.add_dependency "fog-aws"
19
19
  spec.add_dependency "mime-types"
20
+ spec.add_dependency "activerecord", ">= 4.0"
21
+ spec.add_dependency "activesupport", ">= 4.0"
22
+
20
23
  spec.add_development_dependency "bundler"
21
- spec.add_development_dependency "rake"
22
24
  spec.add_development_dependency "rspec"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "sqlite3"
27
+ spec.add_development_dependency "appraisal"
23
28
  end
@@ -11,8 +11,8 @@ describe "access to model data from uploaders" do
11
11
  }
12
12
 
13
13
  let(:klass) {
14
- klass = Class.new {
15
- include Saviour::BasicModel
14
+ klass = Class.new(Test) {
15
+ include Saviour::Model
16
16
 
17
17
  def id
18
18
  87
@@ -0,0 +1,143 @@
1
+ require 'spec_helper'
2
+
3
+ describe "saving a new file" do
4
+ before { allow(Saviour::Config).to receive(:storage).and_return(Saviour::LocalStorage.new(local_prefix: @tmpdir, public_url_prefix: "http://domain.com")) }
5
+
6
+ let(:uploader) {
7
+ Class.new(Saviour::BaseUploader) {
8
+ store_dir { "/store/dir" }
9
+ }
10
+ }
11
+
12
+ let(:klass) {
13
+ a = Class.new(Test) { include Saviour::Model }
14
+ a.attach_file :file, uploader
15
+ a
16
+ }
17
+
18
+ describe "creation" do
19
+ it do
20
+ with_test_file("example.xml") do |example|
21
+ a = klass.create!
22
+ expect(a.update_attributes(file: example)).to be_truthy
23
+ end
24
+ end
25
+
26
+ it do
27
+ with_test_file("example.xml") do |example|
28
+ a = klass.create!
29
+ a.update_attributes(file: example)
30
+
31
+ expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
32
+ end
33
+ end
34
+
35
+ it do
36
+ with_test_file("example.xml") do |example, real_filename|
37
+ a = klass.create!
38
+ a.update_attributes(file: example)
39
+ expect(a[:file]).to eq "/store/dir/#{real_filename}"
40
+ end
41
+ end
42
+
43
+ it do
44
+ with_test_file("example.xml") do |example|
45
+ a = klass.create!
46
+ a.update_attributes(file: example)
47
+
48
+ example.rewind
49
+ expect(a.file.read).to eq example.read
50
+ end
51
+ end
52
+
53
+ it do
54
+ with_test_file("example.xml") do |example|
55
+ a = klass.create!
56
+ a.update_attributes(file: example)
57
+
58
+ expect(a.file.exists?).to be_truthy
59
+ end
60
+ end
61
+
62
+ it do
63
+ with_test_file("example.xml") do |example, real_filename|
64
+ a = klass.create!
65
+ a.update_attributes(file: example)
66
+
67
+ expect(a.file.filename).to eq real_filename
68
+ end
69
+ end
70
+
71
+ it do
72
+ with_test_file("example.xml") do |example, real_filename|
73
+ a = klass.create!
74
+ a.update_attributes(file: example)
75
+
76
+ expect(a.file.url).to eq "http://domain.com/store/dir/#{real_filename}"
77
+ expect(a.file.public_url).to eq a.file.url
78
+ end
79
+ end
80
+
81
+ it "don't create anything if save do not completes (halt during before_save)" do
82
+ klass = Class.new(Test) do
83
+ attr_accessor :fail_at_save
84
+ before_save {
85
+ if ActiveRecord.version >= Gem::Version.new("5.0")
86
+ throw(:abort) if fail_at_save
87
+ else
88
+ !fail_at_save
89
+ end
90
+ }
91
+ include Saviour::Model
92
+ end
93
+ klass.attach_file :file, uploader
94
+
95
+ expect {
96
+ a = klass.new
97
+ a.fail_at_save = true
98
+ a.save!
99
+ }.to raise_error(ActiveRecord::RecordNotSaved)
100
+
101
+ with_test_file("example.xml") do |example, _|
102
+ a = klass.new
103
+ a.fail_at_save = true
104
+ a.file = example
105
+
106
+ expect(Saviour::Config.storage).not_to receive(:write)
107
+ a.save
108
+ end
109
+ end
110
+ end
111
+
112
+ describe "deletion" do
113
+ it do
114
+ with_test_file("example.xml") do |example|
115
+ a = klass.create!
116
+ a.update_attributes(file: example)
117
+ expect(a.file.exists?).to be_truthy
118
+ expect(a.destroy).to be_truthy
119
+
120
+ expect(Saviour::Config.storage.exists?(a[:file])).to be_falsey
121
+ end
122
+ end
123
+ end
124
+
125
+ describe "updating" do
126
+ it do
127
+ with_test_file("example.xml") do |example|
128
+ a = klass.create!
129
+ a.update_attributes(file: example)
130
+
131
+ expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
132
+ previous_location = a[:file]
133
+
134
+ with_test_file("camaloon.jpg") do |example_2|
135
+ a.update_attributes(file: example_2)
136
+ expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
137
+
138
+ expect(Saviour::Config.storage.exists?(previous_location)).to be_falsey
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end