rainbow_storage 1.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0c221a06f5c7d6ac3cbbada2cfa3d0f5c02c3d97
4
+ data.tar.gz: f5016782b665e2df5c3f1c6f75a30cba8b63c724
5
+ SHA512:
6
+ metadata.gz: 9a18d1deae6e864974972b6e88f024e9bf9df1bee13396fceed742b9cd63b6f66a933952e961a0c25afd77b16f3b5e33b77c19630e7eab6576750634fcb7981f
7
+ data.tar.gz: b8d4a7113b95c27ae8fb65b1023907632b8df7a6fa03f2e066ad0707ccfbb444c28aad64d713a2256a8afaace3145444a06da85458dd26b0d7015aa1468e6c88
data/README.md ADDED
File without changes
data/TODO.md ADDED
File without changes
@@ -0,0 +1,16 @@
1
+ require 'rainbow_storage/configuration'
2
+ require 'rainbow_storage/manager'
3
+ require 'rainbow_storage/path_generator'
4
+
5
+ module RainbowStorage
6
+ class << self
7
+ def manager
8
+ fail StorageNotConfiguredException unless config
9
+ RainbowStorage::Manager.new(config, path_generator)
10
+ end
11
+
12
+ def path_generator
13
+ RainbowStorage::PathGenerator.new(config)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,34 @@
1
+ module RainbowStorage
2
+ module Adapters
3
+ class AbstractAdapter
4
+ attr_accessor :config
5
+
6
+ def initialize(config)
7
+ @config = config
8
+ post_initialize
9
+ end
10
+
11
+ def get(path)
12
+ fail NotImplementedError
13
+ end
14
+
15
+ def put(path, data)
16
+ fail NotImplementedError
17
+ end
18
+
19
+ def delete(path)
20
+ fail NotImplementedError
21
+ end
22
+
23
+ def public_url(path)
24
+ fail NotImplementedError
25
+ end
26
+
27
+ private
28
+
29
+ # Hook-method to add custom initialization logic to adapter
30
+ def post_initialize
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,19 @@
1
+ require 'rainbow_storage/exceptions/unknown_adapter_exception'
2
+
3
+ module RainbowStorage
4
+ module Adapters
5
+ class Repository
6
+ class << self
7
+ def get(name)
8
+ adapters[name] || fail(RainbowStorage::UnknownAdapterException, "Unknown adapter '#{name}'")
9
+ end
10
+
11
+ def adapters
12
+ {
13
+ s3: RainbowStorage::Adapters::S3::Adapter
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ require 'rainbow_storage/adapters/s3/adapter'
2
+
3
+ module RainbowStorage
4
+ module Adapters
5
+ module S3
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,46 @@
1
+ require 's3'
2
+ require 'rainbow_storage/adapters/abstract_adapter'
3
+
4
+ module RainbowStorage
5
+ module Adapters
6
+ module S3
7
+ class Adapter < RainbowStorage::Adapters::AbstractAdapter
8
+ attr_accessor :bucket
9
+
10
+ # TODO: should we deal with tempfiles here? or just return a data?
11
+ def get(path)
12
+ object = bucket.objects.find(path)
13
+ tempfile = Tempfile.new
14
+ tempfile.write(object.content)
15
+ tempfile.rewind
16
+ tempfile
17
+ end
18
+
19
+ def put(path, data)
20
+ new_object = bucket.objects.build(path)
21
+ new_object.content = data
22
+ new_object.acl = :public_read
23
+ new_object.save
24
+ end
25
+
26
+ def delete(path)
27
+ object = bucket.objects.find(path)
28
+ object.destroy
29
+ end
30
+
31
+ def public_url(path)
32
+ object = bucket.objects.find(path)
33
+ object.temporary_url(Time.now + 1800)
34
+ end
35
+
36
+ private
37
+
38
+ def post_initialize
39
+ @service = ::S3::Service.new(access_key_id: config[:access_key],
40
+ secret_access_key: config[:secret_key])
41
+ @bucket = @service.bucket(config[:bucket])
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,20 @@
1
+ module RainbowStorage
2
+ class Configuration
3
+ attr_accessor :salt, :nesting, :adapter, :adapter_config
4
+
5
+ def initialize
6
+ @salt = 'default_salt'
7
+ @nesting = 3
8
+ end
9
+ end
10
+
11
+ class << self
12
+ def setup
13
+ yield(config)
14
+ end
15
+
16
+ def config
17
+ @config ||= RainbowStorage::Configuration.new
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module RainbowStorage
2
+ class StorageNotConfiguredException < StandardError; end;
3
+ end
@@ -0,0 +1,3 @@
1
+ module RainbowStorage
2
+ class UnknownAdapterException < StandardError; end;
3
+ end
@@ -0,0 +1,3 @@
1
+ module RainbowStorage
2
+ class UnsupportedFileTypeException < StandardError; end;
3
+ end
@@ -0,0 +1,73 @@
1
+ require 'rainbow_storage/adapters/s3'
2
+ require 'rainbow_storage/adapters/repository'
3
+ require 'rainbow_storage/exceptions/unsupported_file_type_exception'
4
+ require 'open-uri'
5
+
6
+ module RainbowStorage
7
+ class Manager
8
+ attr_accessor :config, :path_generator
9
+
10
+ def initialize(config, path_generator)
11
+ @config = config
12
+ @path_generator = path_generator
13
+ end
14
+
15
+ # TODO: maybe introduce RainbowStorage::File class for path, extension, content and content-type?
16
+ # Usage examples:
17
+ # RainbowStorage.manager.upload({file: URI('http://site.com/files/1358_1495284100.jpg'), id: 1, extension: '.jpg'})
18
+ def upload(options = {})
19
+ validate_upload_options!(options)
20
+
21
+ file = options[:file]
22
+ id = options[:id] || "#{file.object_id}#{Time.now.to_i}"
23
+ extension = options[:extension] || infer_extenstion_from_file(file)
24
+
25
+ content = case file
26
+ when Tempfile
27
+ file.rewind
28
+ file.read
29
+ when URI
30
+ open(file.to_s).read
31
+ else
32
+ fail RainbowStorage::UnsupportedFileTypeException,
33
+ "#{file.class} is not valid type for file attribute"
34
+ end
35
+
36
+ path = path_generator.filename(id, extension)
37
+ adapter.put(path, content)
38
+ end
39
+
40
+ # Usage example:
41
+ # RainbowStorage.manager.download(1, '.jpg')
42
+ def download(id, extension = nil)
43
+ path = path_generator.filename(id, extension)
44
+ adapter.get(path)
45
+ end
46
+
47
+ # Usage example:
48
+ # RainbowStorage.manager.remove(1, 'jpg')
49
+ def remove(id, extension = nil)
50
+ path = path_generator.filename(id, extension)
51
+ adapter.delete(path)
52
+ end
53
+
54
+ # Usage example:
55
+ # RainbowStorage.manager.link(1, 'jpg')
56
+ def link(id, extension = nil)
57
+ path = path_generator.filename(id, extension)
58
+ adapter.public_url(path)
59
+ end
60
+
61
+ private
62
+
63
+ def validate_upload_options!(options)
64
+ end
65
+
66
+ def infer_extension_from_file(file)
67
+ end
68
+
69
+ def adapter
70
+ @adapter ||= RainbowStorage::Adapters::Repository.get(config.adapter).new(config.adapter_config)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,42 @@
1
+ module RainbowStorage
2
+ class PathGenerator
3
+ attr_accessor :config
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ # Add extension to path if present
10
+ # do not add extra dot before extesion
11
+ def filename(id, extension)
12
+ path = filepath(id)
13
+ return path if extension.nil?
14
+ "#{path}.#{extension.gsub(/^\./, '')}"
15
+ end
16
+
17
+ # Convert string like `abcdefghi` to `ab/cd/ef/ghi`
18
+ # to represent location on file system
19
+ def filepath(id)
20
+ tokens = hmac(id).chars.each_slice(2).map(&:join)
21
+ result = []
22
+ nesting_depth.times { result << tokens.shift }
23
+ result << tokens.join
24
+ result.join('/')
25
+ end
26
+
27
+ private
28
+
29
+ def hmac(id)
30
+ digest = OpenSSL::Digest.new('sha1')
31
+ OpenSSL::HMAC.hexdigest(digest, hmac_key, id.to_s)
32
+ end
33
+
34
+ def hmac_key
35
+ config.salt
36
+ end
37
+
38
+ def nesting_depth
39
+ config.nesting
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "rainbow_storage"
3
+ s.version = "1.0.1"
4
+ s.licenses = ['MIT']
5
+ s.summary = "This is an example!"
6
+ s.description = "Much longer explanation of the example!"
7
+ s.author = "Stanislav Mekhonoshin"
8
+ s.email = "ejabberd@gmail.com"
9
+ s.require_paths = ["lib"]
10
+ s.files = `git ls-files`.split($/)
11
+
12
+ s.add_runtime_dependency "s3", ["= 0.3.25"]
13
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rainbow_storage
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Stanislav Mekhonoshin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-06-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: s3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.25
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.25
27
+ description: Much longer explanation of the example!
28
+ email: ejabberd@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - README.md
34
+ - TODO.md
35
+ - lib/rainbow_storage.rb
36
+ - lib/rainbow_storage/adapters/abstract_adapter.rb
37
+ - lib/rainbow_storage/adapters/repository.rb
38
+ - lib/rainbow_storage/adapters/s3.rb
39
+ - lib/rainbow_storage/adapters/s3/adapter.rb
40
+ - lib/rainbow_storage/configuration.rb
41
+ - lib/rainbow_storage/exceptions/storage_not_configured_exception.rb
42
+ - lib/rainbow_storage/exceptions/unknown_adapter_exception.rb
43
+ - lib/rainbow_storage/exceptions/unsupported_file_type_exception.rb
44
+ - lib/rainbow_storage/manager.rb
45
+ - lib/rainbow_storage/path_generator.rb
46
+ - rainbow_storage.gemspec
47
+ homepage:
48
+ licenses:
49
+ - MIT
50
+ metadata: {}
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 2.5.1
68
+ signing_key:
69
+ specification_version: 4
70
+ summary: This is an example!
71
+ test_files: []