saviour 0.2.3 → 0.3.0

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