saviour 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+
3
+ describe Saviour do
4
+ it "raises error if included in a non active record class" do
5
+ expect {
6
+ Class.new do
7
+ include Saviour::Model
8
+ end
9
+ }.to raise_error(Saviour::NoActiveRecordDetected)
10
+ end
11
+
12
+ it "error if column not present" do
13
+ expect {
14
+ Class.new(Test) do
15
+ include Saviour::Model
16
+
17
+ attach_file :not_present, Saviour::BaseUploader
18
+ end
19
+ }.to raise_error(RuntimeError)
20
+ end
21
+
22
+ context do
23
+ it "error if column not present on version" do
24
+ uploader = Class.new(Saviour::BaseUploader) do
25
+ store_dir { "/store/dir" }
26
+
27
+ version(:thumb) do
28
+ store_dir { "/versions/store/dir" }
29
+ end
30
+
31
+ version(:not_present)
32
+ end
33
+
34
+ expect {
35
+ Class.new(Test) do
36
+ include Saviour::Model
37
+
38
+ attach_file :file, uploader
39
+ end
40
+ }.to raise_error(RuntimeError)
41
+ end
42
+ end
43
+
44
+ it "does not raise error if table is not present" do
45
+ allow(Test).to receive(:table_exists?).and_return(false)
46
+
47
+ expect {
48
+ Class.new(Test) do
49
+ include Saviour::Model
50
+
51
+ attach_file :not_present, Saviour::BaseUploader
52
+ end
53
+ }.to_not raise_error
54
+ end
55
+
56
+ describe ".attached_files" do
57
+ it "includes a mapping of the currently attached files and their versions" do
58
+ uploader = Class.new(Saviour::BaseUploader) do
59
+ store_dir { "/store/dir" }
60
+
61
+ version(:thumb)
62
+ version(:thumb_2)
63
+ end
64
+
65
+ klass = Class.new(Test) do
66
+ include Saviour::Model
67
+ attach_file :file, uploader
68
+ end
69
+
70
+ expect(klass.attached_files).to eq({file: [:thumb, :thumb_2]})
71
+
72
+ klass2 = Class.new(Test) do
73
+ include Saviour::Model
74
+ attach_file :file, Saviour::BaseUploader
75
+ end
76
+
77
+ expect(klass2.attached_files).to eq({file: []})
78
+ end
79
+ end
80
+
81
+ describe ".attached_files" do
82
+ it "includes a mapping of the currently attached files and their versions" do
83
+ uploader = Class.new(Saviour::BaseUploader) do
84
+ store_dir { "/store/dir" }
85
+
86
+ version(:thumb)
87
+ version(:thumb_2)
88
+ end
89
+
90
+ klass = Class.new(Test) do
91
+ include Saviour::Model
92
+ attach_file :file, uploader
93
+ end
94
+
95
+ expect(klass.attached_files).to eq({file: [:thumb, :thumb_2]})
96
+
97
+ klass2 = Class.new(Test) do
98
+ include Saviour::Model
99
+ attach_file :file, Saviour::BaseUploader
100
+ end
101
+
102
+ expect(klass2.attached_files).to eq({file: []})
103
+ end
104
+ end
105
+
106
+ it "doens't mess with default File constant" do
107
+ # Constant lookup in ruby works by lexical scope, so we can't create classes dynamically like above.
108
+ expect(TestForSaviourFileResolution.new.foo).to be_falsey
109
+ end
110
+
111
+ it "shares model definitions with subclasses" do
112
+ uploader = Class.new(Saviour::BaseUploader) do
113
+ store_dir { "/store/dir" }
114
+ version(:thumb)
115
+ end
116
+
117
+ klass = Class.new(Test) do
118
+ include Saviour::Model
119
+ attach_file :file, uploader
120
+ end
121
+ expect(klass.attached_files).to eq({file: [:thumb]})
122
+
123
+ klass2 = Class.new(klass)
124
+ expect(klass2.attached_files).to eq({file: [:thumb]})
125
+
126
+ expect(klass2.new.file).to respond_to :exists?
127
+ end
128
+ end
@@ -11,12 +11,6 @@ describe Saviour::S3Storage do
11
11
  Saviour::S3Storage.new(bucket: "fake-bucket")
12
12
  }.to raise_error(ArgumentError)
13
13
  end
14
-
15
- it "fails when the bucket doesn't exists" do
16
- expect {
17
- Saviour::S3Storage.new(bucket: "no-bucket", aws_access_key_id: "stub", aws_secret_access_key: "stub")
18
- }.to raise_error(ArgumentError)
19
- end
20
14
  end
21
15
 
22
16
  describe "#write" do
data/spec/spec_helper.rb CHANGED
@@ -3,9 +3,34 @@ SimpleCov.start
3
3
 
4
4
  require 'bundler/setup'
5
5
  require 'rspec'
6
+ require 'active_record'
7
+ require 'sqlite3'
6
8
 
7
9
  require File.expand_path("../../lib/saviour", __FILE__)
8
10
 
11
+ connection_opts = case ENV.fetch('DB', "sqlite")
12
+ when "sqlite"
13
+ {adapter: "sqlite3", database: ":memory:"}
14
+ when "mysql"
15
+ {adapter: "mysql2", database: "saviour", username: "root", encoding: "utf8"}
16
+ when "postgres"
17
+ {adapter: "postgresql", database: "saviour", username: "postgres"}
18
+ end
19
+
20
+ ActiveRecord::Base.establish_connection(connection_opts)
21
+
22
+ def silence_stream(stream)
23
+ old_stream = stream.dup
24
+ stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
25
+ stream.sync = true
26
+ yield
27
+ ensure
28
+ stream.reopen(old_stream)
29
+ old_stream.close
30
+ end
31
+
32
+ silence_stream(STDOUT) { require 'support/schema' }
33
+
9
34
  require 'support/models'
10
35
 
11
36
  RSpec.configure do |config|
@@ -1,8 +1,12 @@
1
+ class Test < ActiveRecord::Base
2
+
3
+ end
4
+
1
5
  # Constant lookup in ruby works by lexical scope, so we can't create classes dynamically to test this.
2
- class TestForSaviourFileResolution
3
- include Saviour::BasicModel
6
+ class TestForSaviourFileResolution < Test
7
+ include Saviour::Model
4
8
 
5
9
  def foo
6
10
  File.file?("/tasdasdasdmp/blabla.txt")
7
11
  end
8
- end
12
+ end
@@ -0,0 +1,9 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table :tests do |t|
3
+ t.string :file
4
+ t.string :file_thumb
5
+ t.string :file_thumb_2
6
+ t.string :name
7
+ t.timestamps null: false
8
+ end
9
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saviour
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roger Campos
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-03 00:00:00.000000000 Z
11
+ date: 2017-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fog-aws
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activerecord
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '4.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '4.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: bundler
43
71
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +80,20 @@ dependencies:
52
80
  - - ">="
53
81
  - !ruby/object:Gem::Version
54
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
55
97
  - !ruby/object:Gem::Dependency
56
98
  name: rake
57
99
  requirement: !ruby/object:Gem::Requirement
@@ -67,7 +109,21 @@ dependencies:
67
109
  - !ruby/object:Gem::Version
68
110
  version: '0'
69
111
  - !ruby/object:Gem::Dependency
70
- name: rspec
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: appraisal
71
127
  requirement: !ruby/object:Gem::Requirement
72
128
  requirements:
73
129
  - - ">="
@@ -89,20 +145,25 @@ extra_rdoc_files: []
89
145
  files:
90
146
  - ".gitignore"
91
147
  - ".travis.yml"
92
- - DECOMPOSE.md
148
+ - Appraisals
93
149
  - Gemfile
94
150
  - LICENSE.txt
95
151
  - README.md
96
152
  - Rakefile
153
+ - gemfiles/4.0.gemfile
154
+ - gemfiles/4.1.gemfile
155
+ - gemfiles/4.2.gemfile
156
+ - gemfiles/5.0.gemfile
97
157
  - lib/saviour.rb
98
158
  - lib/saviour/attribute_name_calculator.rb
99
- - lib/saviour/base_integrator.rb
100
159
  - lib/saviour/base_uploader.rb
101
- - lib/saviour/basic_model.rb
102
160
  - lib/saviour/config.rb
103
161
  - lib/saviour/file.rb
162
+ - lib/saviour/integrator.rb
104
163
  - lib/saviour/life_cycle.rb
105
164
  - lib/saviour/local_storage.rb
165
+ - lib/saviour/model.rb
166
+ - lib/saviour/persistence_layer.rb
106
167
  - lib/saviour/s3_storage.rb
107
168
  - lib/saviour/source_filename_extractor.rb
108
169
  - lib/saviour/string_source.rb
@@ -110,17 +171,21 @@ files:
110
171
  - lib/saviour/uploader/processors_runner.rb
111
172
  - lib/saviour/uploader/store_dir_extractor.rb
112
173
  - lib/saviour/url_source.rb
113
- - lib/saviour/utils/class_attribute.rb
174
+ - lib/saviour/validator.rb
114
175
  - lib/saviour/version.rb
115
176
  - saviour.gemspec
116
177
  - spec/feature/access_to_model_and_mounted_as_spec.rb
178
+ - spec/feature/crud_workflows_spec.rb
179
+ - spec/feature/persisted_path_spec.rb
180
+ - spec/feature/reload_model_spec.rb
181
+ - spec/feature/validations_spec.rb
117
182
  - spec/feature/versions_spec.rb
118
183
  - spec/models/attribute_name_calculator_spec.rb
119
184
  - spec/models/base_uploader_spec.rb
120
- - spec/models/basic_model_spec.rb
121
185
  - spec/models/config_spec.rb
122
186
  - spec/models/file_spec.rb
123
187
  - spec/models/local_storage_spec.rb
188
+ - spec/models/model_spec.rb
124
189
  - spec/models/s3_storage_spec.rb
125
190
  - spec/models/url_source_spec.rb
126
191
  - spec/spec_helper.rb
@@ -128,6 +193,7 @@ files:
128
193
  - spec/support/data/example.xml
129
194
  - spec/support/data/text.txt
130
195
  - spec/support/models.rb
196
+ - spec/support/schema.rb
131
197
  homepage: https://github.com/rogercampos/saviour
132
198
  licenses:
133
199
  - MIT
@@ -148,7 +214,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
214
  version: '0'
149
215
  requirements: []
150
216
  rubyforge_project:
151
- rubygems_version: 2.5.1
217
+ rubygems_version: 2.4.5.1
152
218
  signing_key:
153
219
  specification_version: 4
154
220
  summary: File storage handler following active record model lifecycle
data/DECOMPOSE.md DELETED
@@ -1,66 +0,0 @@
1
- - New saviour-ar gem will provide what's currently in saviour gem
2
- - saviour gem will be data-storage independent. It will provide a way to manage files with processors, but without a
3
- lifecycle attached to a database-model. It will provide a more low level api.
4
- - saviour-ar will provide the specific integration with active record.
5
-
6
-
7
- # Saviour agnostic gem API
8
-
9
-
10
- First, you'll need to define what files can be saved in what objects (any Ruby class). You can do that by including the `Saviour::BasicModel` module, example:
11
-
12
- ```
13
- class MyObject
14
- include Saviour::BasicModel
15
-
16
- attach_file :image, ImageUploader
17
- attach_file :scheme, FileUploader
18
- end
19
- ```
20
-
21
- Now, you can assign and work with the files associated to instances of MyObject with the following api:
22
-
23
- ```
24
- # New file
25
-
26
- a = MyObject.new
27
- a.image = File.open('/path/image.jpg')
28
- a.image.assign File.open('newfile.jpg')
29
- a.image.changed? # => true
30
- saved_path = a.image.write # => persists file in the storage and returns the path in which the file has been saved
31
-
32
- b = MyObject.new
33
- b.image.set_path!(saved_path) # => Link this image to the persisted image from before
34
- b.image.exists? # => true
35
- b.image.read # -> return bytes
36
- b.image.delete # -> delete
37
- b.image.exists? # => false
38
-
39
- # ...
40
- ```
41
-
42
- If you want to work directly managing the files associated to those objects, you can use the `Saviour::File` public API directly.
43
-
44
- However, Saviour is designed to work with models that are saved in some kind of persistent storage, like a database of some sort. This is why Saviour also provides a generic `LifeCycle` service which you can use to simulate the persistence lifecycle of the object. You can then use:
45
-
46
- ```
47
- a = MyObject.new image: File.open('image.jpg')
48
-
49
- Saviour::LifeCycle.new(a).save!
50
- Saviour::LifeCycle.new(a).delete!
51
- ```
52
-
53
- `save!` will have the effect of saving all the attachments associated with the object, and `delete!` will have the effect of removing all the files associated with this object from the file storage defined.
54
-
55
- Using LifeCycle you consider the object as a whole, while working with individual files you have more control, but you always operate with individual files.
56
-
57
- The feature of versions, for example, only applies when you use the LifeCycle approach, since, by definition, a version is automatically constructed from the original file while this one is saved, and this involved operating in two or more attachments at the same time over an specific object. Since in Saviour versions can be managed exactly like regular attachments, such behavior don't apply when you work with File instances directly.
58
-
59
-
60
- # Saviour API for developers
61
-
62
- If you want to develop a new gem to integrate Saviour with another persistence technology, you need to do two things.
63
-
64
- 1) Write a class with the api #read, #write and #persisted? and give it to Savior, so he knows how to work with the persistence layer.
65
- 2) Write a new module for the final users to include in their models, providing the expected hooks so that the usage of the `LifeCycle` is automatic and transparent.
66
-
@@ -1,7 +0,0 @@
1
- module Saviour
2
- module BasicModel
3
- def self.included(klass)
4
- BaseIntegrator.new(klass).setup!
5
- end
6
- end
7
- end
@@ -1,26 +0,0 @@
1
- # Port and simplification of ActiveSupport class attribute
2
- module Saviour
3
- module ClassAttribute
4
- def class_attribute(*attrs)
5
- attrs.each do |name|
6
- singleton_class.instance_eval do
7
- undef_method(name) if method_defined?(name)
8
- end
9
-
10
- define_singleton_method(name) { nil }
11
-
12
- singleton_class.instance_eval do
13
- undef_method("#{name}=") if method_defined?("#{name}=")
14
- end
15
-
16
- define_singleton_method("#{name}=") do |val|
17
- singleton_class.class_eval do
18
- undef_method(name) if method_defined?(name)
19
- define_method(name) { val }
20
- end
21
- val
22
- end
23
- end
24
- end
25
- end
26
- end