saviour 0.2.3 → 0.3.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +4 -13
  4. data/DECOMPOSE.md +66 -0
  5. data/Gemfile +1 -0
  6. data/README.md +39 -8
  7. data/lib/saviour/attribute_name_calculator.rb +15 -0
  8. data/lib/saviour/base_integrator.rb +53 -0
  9. data/lib/saviour/basic_model.rb +7 -0
  10. data/lib/saviour/config.rb +0 -1
  11. data/lib/saviour/file.rb +13 -34
  12. data/lib/saviour/life_cycle.rb +57 -0
  13. data/lib/saviour/source_filename_extractor.rb +21 -0
  14. data/lib/saviour/url_source.rb +1 -1
  15. data/lib/saviour/utils/class_attribute.rb +26 -0
  16. data/lib/saviour/version.rb +1 -1
  17. data/lib/saviour.rb +7 -155
  18. data/saviour.gemspec +1 -5
  19. data/spec/feature/access_to_model_and_mounted_as_spec.rb +13 -5
  20. data/spec/feature/versions_spec.rb +72 -49
  21. data/spec/models/attribute_name_calculator_spec.rb +11 -0
  22. data/spec/models/basic_model_spec.rb +51 -0
  23. data/spec/models/file_spec.rb +32 -55
  24. data/spec/models/url_source_spec.rb +5 -5
  25. data/spec/spec_helper.rb +2 -30
  26. data/spec/support/models.rb +7 -2
  27. metadata +12 -72
  28. data/Appraisals +0 -19
  29. data/gemfiles/4.0.gemfile +0 -9
  30. data/gemfiles/4.1.gemfile +0 -9
  31. data/gemfiles/4.2.gemfile +0 -9
  32. data/gemfiles/5.0.gemfile +0 -9
  33. data/lib/saviour/processors/digest.rb +0 -16
  34. data/spec/feature/crud_workflows_spec.rb +0 -143
  35. data/spec/feature/persisted_path_spec.rb +0 -34
  36. data/spec/feature/reload_model_spec.rb +0 -24
  37. data/spec/feature/validations_spec.rb +0 -171
  38. data/spec/models/processors/digest_spec.rb +0 -22
  39. data/spec/models/saviour_spec.rb +0 -80
  40. data/spec/support/schema.rb +0 -9
data/lib/saviour.rb CHANGED
@@ -1,11 +1,7 @@
1
1
  require 'fileutils'
2
- require 'digest/md5'
3
- require 'active_support/concern'
4
- require 'active_support/core_ext'
5
- require 'active_support/core_ext/module/attribute_accessors'
6
2
  require 'fog/aws'
7
3
 
8
- require 'saviour/processors/digest'
4
+ require 'saviour/utils/class_attribute'
9
5
 
10
6
  require 'saviour/version'
11
7
  require 'saviour/base_uploader'
@@ -15,156 +11,12 @@ require 'saviour/s3_storage'
15
11
  require 'saviour/config'
16
12
  require 'saviour/string_source'
17
13
  require 'saviour/url_source'
14
+ require 'saviour/basic_model'
15
+ require 'saviour/base_integrator'
16
+ require 'saviour/attribute_name_calculator'
17
+ require 'saviour/source_filename_extractor'
18
+ require 'saviour/life_cycle'
18
19
 
19
- module Saviour
20
- class ColumnNamer
21
- def initialize(attached_as, version = nil)
22
- @attached_as, @version = attached_as, version
23
- end
24
-
25
- def name
26
- if @version
27
- "#{@attached_as}_#{@version}"
28
- else
29
- @attached_as.to_s
30
- end
31
- end
32
- end
33
-
34
- class ModelHooks
35
- def initialize(model)
36
- @model = model
37
- end
38
-
39
- def delete!
40
- attached_files.each do |column, versions|
41
- (versions + [nil]).each { |version| @model.send(column, version).delete if @model.send(column, version).exists? }
42
- end
43
- end
44
-
45
- def save!
46
- attached_files.each do |column, versions|
47
- base_file_changed = @model.send(column).changed?
48
- original_content = @model.send(column).source_data if base_file_changed
49
-
50
- versions.each do |version|
51
- if @model.send(column, version).changed?
52
- upload_file(column, version)
53
- elsif base_file_changed
54
- @model.send(column, version).assign(StringSource.new(original_content, default_version_filename(column, version)))
55
- upload_file(column, version)
56
- end
57
- end
58
-
59
- upload_file(column, nil) if base_file_changed
60
- end
61
- end
62
-
63
- def validate!
64
- validations.each do |column, method_or_blocks|
65
- (attached_files[column] + [nil]).each do |version|
66
- if @model.send(column, version).changed?
67
- method_or_blocks.each { |method_or_block| run_validation(column, version, method_or_block) }
68
- end
69
- end
70
- end
71
- end
72
-
73
- def default_version_filename(column, version)
74
- filename = @model.send(column).filename_to_be_assigned
75
- "#{::File.basename(filename, ".*")}_#{version}#{::File.extname(filename)}"
76
- end
77
-
78
- def upload_file(column, version)
79
- name = ColumnNamer.new(column, version).name
80
- Config.storage.delete(@model.read_attribute(name)) if @model.read_attribute(name)
81
- new_path = @model.send(column, version).write
82
- @model.update_column(name, new_path)
83
- end
84
-
85
- def attached_files
86
- @model.class.attached_files || {}
87
- end
88
-
89
- def run_validation(column, version, method_or_block)
90
- data = @model.send(column, version).source_data
91
- filename = @model.send(column, version).filename_to_be_assigned
92
- opts = {attached_as: column, version: version}
93
-
94
- if method_or_block.respond_to?(:call)
95
- if method_or_block.arity == 2
96
- @model.instance_exec(data, filename, &method_or_block)
97
- else
98
- @model.instance_exec(data, filename, opts, &method_or_block)
99
- end
100
- else
101
- if @model.method(method_or_block).arity == 2
102
- @model.send(method_or_block, data, filename)
103
- else
104
- @model.send(method_or_block, data, filename, opts)
105
- end
106
- end
107
- end
108
-
109
- def validations
110
- @model.class.__saviour_validations || {}
111
- end
112
- end
113
20
 
114
- extend ActiveSupport::Concern
115
-
116
- NoActiveRecordDetected = Class.new(StandardError)
117
-
118
- included do
119
- raise(NoActiveRecordDetected, "Error: ActiveRecord not detected in #{self}") unless self.ancestors.include?(ActiveRecord::Base)
120
-
121
- class_attribute(:attached_files, :__saviour_validations)
122
-
123
- after_destroy { ModelHooks.new(self).delete! }
124
- after_save { ModelHooks.new(self).save! }
125
- validate { ModelHooks.new(self).validate! }
126
- end
127
-
128
- def reload
129
- self.class.attached_files.each do |attach_as, versions|
130
- (versions + [nil]).each { |version| instance_variable_set("@__uploader_#{version}_#{attach_as}", nil) }
131
- end
132
- super
133
- end
134
-
135
- module ClassMethods
136
- def attach_file(attach_as, uploader_klass)
137
- self.attached_files ||= {}
138
- versions = uploader_klass.versions || []
139
-
140
- ([nil] + versions).each do |version|
141
- column_name = ColumnNamer.new(attach_as, version).name
142
-
143
- if self.table_exists? && !self.column_names.include?(column_name.to_s)
144
- raise RuntimeError, "#{self} must have a database string column named '#{column_name}'"
145
- end
146
- end
147
-
148
- define_method(attach_as) do |version = nil|
149
- instance_variable_get("@__uploader_#{version}_#{attach_as}") ||
150
- instance_variable_set("@__uploader_#{version}_#{attach_as}", ::Saviour::File.new(uploader_klass, self, attach_as, version))
151
- end
152
-
153
- define_method("#{attach_as}=") do |value|
154
- send(attach_as).assign(value)
155
- end
156
-
157
- define_method("#{attach_as}_changed?") do
158
- send(attach_as).changed?
159
- end
160
-
161
- self.attached_files[attach_as] ||= []
162
- self.attached_files[attach_as] += versions
163
- end
164
-
165
- def attach_validation(attach_as, method_name = nil, &block)
166
- self.__saviour_validations ||= Hash.new { [] }
167
- self.__saviour_validations[attach_as] += [method_name || block]
168
- end
169
- end
21
+ module Saviour
170
22
  end
data/saviour.gemspec CHANGED
@@ -13,15 +13,11 @@ Gem::Specification.new do |spec|
13
13
  spec.files = `git ls-files`.split($/)
14
14
  spec.require_paths = ["lib"]
15
15
 
16
- spec.required_ruby_version = ">= 2.0.0"
16
+ spec.required_ruby_version = ">= 2.1.0"
17
17
 
18
- spec.add_dependency "activerecord", ">= 4.0"
19
- spec.add_dependency "activesupport", ">= 4.0"
20
18
  spec.add_dependency "fog-aws"
21
19
  spec.add_dependency "mime-types"
22
20
  spec.add_development_dependency "bundler"
23
21
  spec.add_development_dependency "rake"
24
22
  spec.add_development_dependency "rspec"
25
- spec.add_development_dependency "sqlite3"
26
- spec.add_development_dependency "appraisal"
27
23
  end
@@ -11,7 +11,13 @@ describe "access to model data from uploaders" do
11
11
  }
12
12
 
13
13
  let(:klass) {
14
- klass = Class.new(Test) { include Saviour }
14
+ klass = Class.new {
15
+ include Saviour::BasicModel
16
+
17
+ def id
18
+ 87
19
+ end
20
+ }
15
21
  klass.attach_file :file, uploader
16
22
  klass
17
23
  }
@@ -19,10 +25,12 @@ describe "access to model data from uploaders" do
19
25
  describe "file store" do
20
26
  it do
21
27
  with_test_file("example.xml") do |example, name|
22
- a = klass.create! id: 87
23
- expect(a.update_attributes(file: example)).to be_truthy
24
- expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
25
- expect(a[:file]).to eq "/store/dir/87/87-file-#{name}"
28
+ a = klass.new
29
+ a.file = example
30
+ path = a.file.write
31
+
32
+ expect(Saviour::Config.storage.exists?(path)).to be_truthy
33
+ expect(path).to eq "/store/dir/87/87-file-#{name}"
26
34
  end
27
35
  end
28
36
  end
@@ -14,7 +14,7 @@ describe "saving a new file" do
14
14
  }
15
15
 
16
16
  let(:klass) {
17
- a = Class.new(Test) { include Saviour }
17
+ a = Class.new { include Saviour::BasicModel }
18
18
  a.attach_file :file, uploader
19
19
  a
20
20
  }
@@ -22,9 +22,13 @@ describe "saving a new file" do
22
22
  describe "creation following main file" do
23
23
  it do
24
24
  with_test_file("example.xml") do |example|
25
- a = klass.create!
26
- expect(a.update_attributes(file: example)).to be_truthy
27
- expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
25
+ a = klass.new
26
+ a.file = example
27
+ Saviour::LifeCycle.new(a).save!
28
+
29
+ path = a.file(:thumb).persisted_path
30
+ expect(path).not_to be_nil
31
+ expect(Saviour::Config.storage.exists?(path)).to be_truthy
28
32
  end
29
33
  end
30
34
  end
@@ -32,14 +36,16 @@ describe "saving a new file" do
32
36
  describe "deletion" do
33
37
  it do
34
38
  with_test_file("example.xml") do |example|
35
- a = klass.create!
36
- a.update_attributes(file: example)
37
- expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
38
- expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
39
-
40
- a.destroy
41
- expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_falsey
42
- expect(Saviour::Config.storage.exists?(a[:file])).to be_falsey
39
+ a = klass.new
40
+ a.file = example
41
+ Saviour::LifeCycle.new(a).save!
42
+
43
+ expect(Saviour::Config.storage.exists?(a.file(:thumb).persisted_path)).to be_truthy
44
+ expect(Saviour::Config.storage.exists?(a.file.persisted_path)).to be_truthy
45
+
46
+ Saviour::LifeCycle.new(a).delete!
47
+ expect(Saviour::Config.storage.exists?(a.file(:thumb).persisted_path)).to be_falsey
48
+ expect(Saviour::Config.storage.exists?(a.file.persisted_path)).to be_falsey
43
49
  end
44
50
  end
45
51
  end
@@ -47,13 +53,18 @@ describe "saving a new file" do
47
53
  describe "changes following main file" do
48
54
  it do
49
55
  with_test_file("example.xml") do |example|
50
- a = klass.create!
51
- expect(a.update_attributes(file: example)).to be_truthy
52
- expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
56
+ a = klass.new
57
+ a.file = example
58
+ Saviour::LifeCycle.new(a).save!
59
+ path = a.file(:thumb).persisted_path
60
+ expect(Saviour::Config.storage.exists?(path)).to be_truthy
53
61
 
54
62
  with_test_file("camaloon.jpg") do |file|
55
- a.update_attributes(file: file)
56
- expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
63
+ a.file = file
64
+ Saviour::LifeCycle.new(a).save!
65
+ path = a.file(:thumb).persisted_path
66
+
67
+ expect(Saviour::Config.storage.exists?(path)).to be_truthy
57
68
  file.rewind
58
69
  expect(a.file(:thumb).read).to eq file.read
59
70
  end
@@ -75,8 +86,9 @@ describe "saving a new file" do
75
86
 
76
87
  it "#url" do
77
88
  with_test_file("example.xml") do |example, name|
78
- a = klass.create!
79
- expect(a.update_attributes(file: example)).to be_truthy
89
+ a = klass.new
90
+ a.file = example
91
+ Saviour::LifeCycle.new(a).save!
80
92
 
81
93
  versioned_name = "#{File.basename(name, ".*")}_thumb#{File.extname(name)}"
82
94
  expect(a.file(:thumb).url).to eq "http://domain.com/versions/store/dir/#{versioned_name}"
@@ -85,8 +97,9 @@ describe "saving a new file" do
85
97
 
86
98
  it "#read" do
87
99
  with_test_file("text.txt") do |example|
88
- a = klass.create!
89
- a.update_attributes(file: example)
100
+ a = klass.new
101
+ a.file = example
102
+ Saviour::LifeCycle.new(a).save!
90
103
 
91
104
  expect(a.file(:thumb).read).to eq "Hello world\n_for_version_thumb"
92
105
  end
@@ -94,21 +107,24 @@ describe "saving a new file" do
94
107
 
95
108
  it "#delete" do
96
109
  with_test_file("example.xml") do |example|
97
- a = klass.create!
98
- expect(a.update_attributes(file: example)).to be_truthy
99
- expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
100
- expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
110
+ a = klass.new
111
+ a.file = example
112
+ Saviour::LifeCycle.new(a).save!
113
+ expect(Saviour::Config.storage.exists?(a.file(:thumb).persisted_path)).to be_truthy
114
+ expect(Saviour::Config.storage.exists?(a.file.persisted_path)).to be_truthy
101
115
 
102
116
  a.file(:thumb).delete
103
- expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_falsey
104
- expect(Saviour::Config.storage.exists?(a[:file])).to be_truthy
117
+
118
+ expect(Saviour::Config.storage.exists?(a.file(:thumb).persisted_path)).to be_falsey
119
+ expect(Saviour::Config.storage.exists?(a.file.persisted_path)).to be_truthy
105
120
  end
106
121
  end
107
122
 
108
123
  it "#exists?" do
109
124
  with_test_file("example.xml") do |example|
110
- a = klass.create!
111
- expect(a.update_attributes(file: example)).to be_truthy
125
+ a = klass.new
126
+ a.file = example
127
+ Saviour::LifeCycle.new(a).save!
112
128
  expect(a.file(:thumb).exists?).to be_truthy
113
129
  end
114
130
  end
@@ -117,18 +133,21 @@ describe "saving a new file" do
117
133
  describe "assign specific version after first creation" do
118
134
  it do
119
135
  with_test_file("example.xml") do |example|
120
- a = klass.create!
121
- expect(a.update_attributes(file: example)).to be_truthy
122
- expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
123
- expect(a[:file_thumb]).to eq "/versions/store/dir/#{File.basename(example, ".*")}_thumb.xml"
136
+ a = klass.new
137
+ a.file = example
138
+ Saviour::LifeCycle.new(a).save!
139
+
140
+ thumb_path = a.file(:thumb).persisted_path
141
+ expect(Saviour::Config.storage.exists?(thumb_path)).to be_truthy
142
+ expect(thumb_path).to eq "/versions/store/dir/#{File.basename(example, ".*")}_thumb.xml"
124
143
 
125
144
  with_test_file("camaloon.jpg") do |ex2, filename|
126
145
  a.file(:thumb).assign(ex2)
146
+ Saviour::LifeCycle.new(a).save!
147
+ thumb_path = a.file(:thumb).persisted_path
127
148
 
128
- expect(a.save!).to be_truthy
129
-
130
- expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
131
- expect(a[:file_thumb]).to eq "/versions/store/dir/#{File.basename(filename, ".*")}.jpg"
149
+ expect(Saviour::Config.storage.exists?(thumb_path)).to be_truthy
150
+ expect(thumb_path).to eq "/versions/store/dir/#{File.basename(filename, ".*")}.jpg"
132
151
  end
133
152
  end
134
153
  end
@@ -147,19 +166,23 @@ describe "saving a new file" do
147
166
 
148
167
  it "runs the processors for that version only" do
149
168
  with_test_file("example.xml") do |example|
150
- a = klass.create!
151
- expect(a.update_attributes(file: example)).to be_truthy
152
- expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
153
- expect(a[:file_thumb]).to eq "/versions/store/dir/#{File.basename(example, ".*")}_thumb.xml"
169
+ a = klass.new
170
+ a.file = example
171
+ Saviour::LifeCycle.new(a).save!
172
+ thumb_path = a.file(:thumb).persisted_path
173
+
174
+ expect(Saviour::Config.storage.exists?(thumb_path)).to be_truthy
175
+ expect(thumb_path).to eq "/versions/store/dir/#{File.basename(example, ".*")}_thumb.xml"
154
176
 
155
177
  with_test_file("camaloon.jpg") do |ex2, filename|
156
178
  a.file(:thumb).assign(ex2)
157
179
 
158
- expect(a.save!).to be_truthy
180
+ Saviour::LifeCycle.new(a).save!
181
+ thumb_path = a.file(:thumb).persisted_path
159
182
 
160
- expect(Saviour::Config.storage.exists?(a[:file_thumb])).to be_truthy
161
- expect(a[:file_thumb]).to eq "/versions/store/dir/#{File.basename(filename, ".*")}.jpg"
162
- expect(Saviour::Config.storage.read(a[:file_thumb])).to eq "modified_content"
183
+ expect(Saviour::Config.storage.exists?(thumb_path)).to be_truthy
184
+ expect(thumb_path).to eq "/versions/store/dir/#{File.basename(filename, ".*")}.jpg"
185
+ expect(Saviour::Config.storage.read(thumb_path)).to eq "modified_content"
163
186
  end
164
187
  end
165
188
  end
@@ -168,16 +191,16 @@ describe "saving a new file" do
168
191
 
169
192
  describe "respects version assignation vs main file assignation on conflict" do
170
193
  it do
171
- a = klass.create!
194
+ a = klass.new
172
195
 
173
196
  with_test_file("example.xml") do |file1, fname1|
174
197
  with_test_file("camaloon.jpg") do |file2, fname2|
175
198
  a.file.assign(file1)
176
199
  a.file(:thumb).assign(file2)
177
- a.save!
200
+ Saviour::LifeCycle.new(a).save!
178
201
 
179
- expect(a[:file]).to eq "/store/dir/#{fname1}"
180
- expect(a[:file_thumb]).to eq "/versions/store/dir/#{fname2}"
202
+ expect(a.file.persisted_path).to eq "/store/dir/#{fname1}"
203
+ expect(a.file(:thumb).persisted_path).to eq "/versions/store/dir/#{fname2}"
181
204
  end
182
205
  end
183
206
  end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Saviour::AttributeNameCalculator do
4
+ it "returns the attached_as value" do
5
+ expect(Saviour::AttributeNameCalculator.new("preview_file").name).to eq "preview_file"
6
+ end
7
+
8
+ it "appends the version if provided" do
9
+ expect(Saviour::AttributeNameCalculator.new("preview_file", "thumb").name).to eq "preview_file_thumb"
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Saviour do
4
+ describe ".attached_files" do
5
+ it "includes a mapping of the currently attached files and their versions" do
6
+ uploader = Class.new(Saviour::BaseUploader) do
7
+ store_dir { "/store/dir" }
8
+
9
+ version(:thumb)
10
+ version(:thumb_2)
11
+ end
12
+
13
+ klass = Class.new do
14
+ include Saviour::BasicModel
15
+ attach_file :file, uploader
16
+ end
17
+
18
+ expect(klass.attached_files).to eq({file: [:thumb, :thumb_2]})
19
+
20
+ klass2 = Class.new do
21
+ include Saviour::BasicModel
22
+ attach_file :file, Saviour::BaseUploader
23
+ end
24
+
25
+ expect(klass2.attached_files).to eq({file: []})
26
+ end
27
+ end
28
+
29
+ it "doens't mess with default File constant" do
30
+ # Constant lookup in ruby works by lexical scope, so we can't create classes dynamically like above.
31
+ expect(TestForSaviourFileResolution.new.foo).to be_falsey
32
+ end
33
+
34
+ it "shares model definitions with subclasses" do
35
+ uploader = Class.new(Saviour::BaseUploader) do
36
+ store_dir { "/store/dir" }
37
+ version(:thumb)
38
+ end
39
+
40
+ klass = Class.new do
41
+ include Saviour::BasicModel
42
+ attach_file :file, uploader
43
+ end
44
+ expect(klass.attached_files).to eq({file: [:thumb]})
45
+
46
+ klass2 = Class.new(klass)
47
+ expect(klass2.attached_files).to eq({file: [:thumb]})
48
+
49
+ expect(klass2.new.file).to respond_to :exists?
50
+ end
51
+ end