paperclip-sftp 1.0.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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Yury Velikanau
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ [![Dependency Status](https://gemnasium.com/spectator/paperclip-sftp.png?travis)](https://gemnasium.com/spectator/paperclip-sftp)
2
+
3
+ Paperclip SFTP
4
+ ==============
5
+
6
+ Paperclip SFTP is Secure File Transfer Protocol storage for [Paperclip](https://github.com/thoughtbot/paperclip)
7
+
8
+ Installation
9
+ ------------
10
+
11
+ ```ruby
12
+ gem 'paperclip-sftp', '~> 1.0.0'
13
+ ```
14
+
15
+ Usage
16
+ -----
17
+
18
+ ```ruby
19
+ class User < ActiveRecord::Base
20
+ has_attached_file :avatar,
21
+ storage: :sftp,
22
+ sftp_options: {
23
+ host: "sftp.example.com",
24
+ user: "user",
25
+ password: "password"
26
+ }
27
+ end
28
+ ```
29
+
30
+ You can define these options globally, enable this storage for specific environments, etc. Please see [Paperclip](https://github.com/thoughtbot/paperclip) github page for more details.
31
+
32
+ Running tests
33
+ -------------
34
+
35
+ All tests are live so in order to run them you need to setup local (or remote) SFTP server. That will take at most 5 minutes on either MacOS or Linux. Connection is defined in `test/test_helper.rb` if you need to change it.
36
+
37
+ Contributing
38
+ ------------
39
+
40
+ 1. Fork it
41
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
42
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
43
+ 4. Push to the branch (`git push origin my-new-feature`)
44
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.pattern = "test/*_test.rb"
8
+ end
9
+
10
+ task default: "test"
@@ -0,0 +1,3 @@
1
+ require "paperclip"
2
+ require "paperclip-sftp/version"
3
+ require "paperclip-sftp/storage/sftp"
@@ -0,0 +1,105 @@
1
+ module Paperclip
2
+ module Storage
3
+ # SFTP (Secure File Transfer Protocol) storage for Paperclip.
4
+ #
5
+ module Sftp
6
+
7
+ # SFTP storage expects a hash with following options:
8
+ # :host, :user, :password.
9
+ #
10
+ def self.extended(base)
11
+ begin
12
+ require "net/sftp"
13
+ rescue LoadError => e
14
+ e.message << "(You may need to install net-sftp gem)"
15
+ raise e
16
+ end unless defined?(Net::SFTP)
17
+
18
+ base.instance_exec do
19
+ @sftp_options = options[:sftp_options] || {}
20
+ end
21
+ end
22
+
23
+ # Make SFTP connection, but use current one if exists.
24
+ #
25
+ def sftp
26
+ @sftp ||= Net::SFTP.start(
27
+ @sftp_options[:host],
28
+ @sftp_options[:user],
29
+ password: @sftp_options[:password]
30
+ )
31
+ end
32
+
33
+ def exists?(style = default_style)
34
+ if original_filename
35
+ files = sftp.dir.entries(File.dirname(path(style))).map(&:name)
36
+ files.include?(File.basename(path(style)))
37
+ else
38
+ false
39
+ end
40
+ rescue Net::SFTP::StatusException => e
41
+ false
42
+ end
43
+
44
+ def copy_to_local_file(style, local_dest_path)
45
+ log("copying #{path(style)} to local file #{local_dest_path}")
46
+ sftp.download!(path(style), local_dest_path)
47
+ rescue Net::SFTP::StatusException => e
48
+ warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
49
+ false
50
+ end
51
+
52
+ def flush_writes #:nodoc:
53
+ @queued_for_write.each do |style, file|
54
+ mkdir_p(File.dirname(path(style)))
55
+ log("uploading #{file.path} to #{path(style)}")
56
+ sftp.upload!(file.path, path(style))
57
+ sftp.setstat!(path(style), :permissions => 0644)
58
+ end
59
+
60
+ after_flush_writes # allows attachment to clean up temp files
61
+ @queued_for_write = {}
62
+ end
63
+
64
+ def flush_deletes #:nodoc:
65
+ @queued_for_delete.each do |path|
66
+ begin
67
+ log("deleting file #{path}")
68
+ sftp.remove(path).wait
69
+ rescue Net::SFTP::StatusException => e
70
+ # ignore file-not-found, let everything else pass
71
+ end
72
+
73
+ begin
74
+ path = File.dirname(path)
75
+ while sftp.dir.entries(path).delete_if { |e| e.name =~ /^\./ }.empty?
76
+ sftp.rmdir(path).wait
77
+ path = File.dirname(path)
78
+ end
79
+ rescue Net::SFTP::StatusException => e
80
+ # stop trying to remove parent directories
81
+ end
82
+ end
83
+
84
+ @queued_for_delete = []
85
+ end
86
+
87
+ private
88
+
89
+ # Create directory structure.
90
+ #
91
+ def mkdir_p(remote_directory)
92
+ log("mkdir_p for #{remote_directory}")
93
+ root_directory = '/'
94
+ remote_directory.split('/').each do |directory|
95
+ next if directory.blank?
96
+ unless sftp.dir.entries(root_directory).map(&:name).include?(directory)
97
+ sftp.mkdir!("#{root_directory}#{directory}")
98
+ end
99
+ root_directory += "#{directory}/"
100
+ end
101
+ end
102
+
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,5 @@
1
+ module Paperclip
2
+ module Sftp
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/paperclip-sftp/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Yury Velikanau"]
6
+ gem.email = ["yury.velikanau@gmail.com"]
7
+ gem.description = %q{SFTP (Secure File Transfer Protocol) storage for Paperclip.}
8
+ gem.summary = %q{SFTP (Secure File Transfer Protocol) storage for Paperclip.}
9
+ gem.homepage = "https://github.com/spectator/paperclip-sftp"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "paperclip-sftp"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Paperclip::Sftp::VERSION
17
+
18
+ gem.add_dependency "paperclip", "~> 3.1.0"
19
+ gem.add_dependency "net-sftp", "~> 2.0.5"
20
+
21
+ gem.add_development_dependency "minitest", "~> 3.3.0"
22
+ gem.add_development_dependency "minitest_should", "~> 0.3.1"
23
+ gem.add_development_dependency "sqlite3", "~> 1.3.3"
24
+ end
Binary file
@@ -0,0 +1,28 @@
1
+ require "test_helper"
2
+
3
+ class IntegrationTest < MiniTest::Should::TestCase
4
+
5
+ setup do
6
+ @file = fixture_file("ruby.png")
7
+ end
8
+
9
+ should "create attachment and its files" do
10
+ dummy = Dummy.create!(avatar: @file)
11
+
12
+ assert File.file?(dummy.avatar.path), "File was not uploaded"
13
+ end
14
+
15
+ should "delete attachment and its files" do
16
+ dummy = Dummy.create!(avatar: @file)
17
+
18
+ avatar = dummy.avatar.path
19
+ dummy.destroy
20
+ assert !File.file?(avatar), "File was not deleted"
21
+ end
22
+
23
+ teardown do
24
+ @file.close
25
+ cleanup
26
+ end
27
+
28
+ end
@@ -0,0 +1,45 @@
1
+ require "paperclip-sftp"
2
+ require "minitest/autorun"
3
+ require "minitest/should"
4
+ require "active_record"
5
+
6
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
7
+
8
+ def define_schema
9
+ ActiveRecord::Schema.define(:version => 1) do
10
+ create_table :dummies do |t|
11
+ t.string :avatar_file_name, :avatar_content_type
12
+ t.integer :avatar_file_size
13
+ end
14
+ end
15
+ end
16
+
17
+ silence_stream(STDOUT) do
18
+ define_schema
19
+ end
20
+
21
+ def fixture_file(file)
22
+ File.new(File.join(File.dirname(__FILE__), "fixtures", file), 'rb')
23
+ end
24
+
25
+ def cleanup
26
+ FileUtils.rm_rf(File.expand_path(File.join(File.dirname(__FILE__), 'tmp')))
27
+ end
28
+
29
+ Paperclip.options[:log] = false
30
+ Paperclip.interpolates(:work_dir) do |attachment, style|
31
+ File.expand_path(File.join(File.dirname(__FILE__), 'tmp'))
32
+ end
33
+
34
+ class Dummy < ActiveRecord::Base
35
+ include Paperclip::Glue
36
+
37
+ has_attached_file :avatar,
38
+ path: ":work_dir/:class/:attachment/:id_partition/:style/:filename",
39
+ storage: :sftp,
40
+ sftp_options: {
41
+ host: "localhost",
42
+ user: "spectator",
43
+ password: "password"
44
+ }
45
+ end
data/test/unit_test.rb ADDED
@@ -0,0 +1,78 @@
1
+ require "test_helper"
2
+
3
+ class UnitTest < MiniTest::Should::TestCase
4
+ setup do
5
+ @file = fixture_file("ruby.png")
6
+ end
7
+
8
+ context "with credentials provided as options" do
9
+ should "persist credentials" do
10
+ dummy = Dummy.new(avatar: @file)
11
+ assert_equal :sftp, dummy.avatar.options[:storage]
12
+ assert_equal "localhost", dummy.avatar.options[:sftp_options][:host]
13
+ assert_equal "spectator", dummy.avatar.options[:sftp_options][:user]
14
+ assert_equal "password", dummy.avatar.options[:sftp_options][:password]
15
+ end
16
+ end
17
+
18
+ context "#exists?" do
19
+ setup do
20
+ @dummy = Dummy.create!(avatar: @file)
21
+ end
22
+
23
+ should "have file" do
24
+ assert @dummy.avatar.exists?, "File doesn't exist in desired location"
25
+ end
26
+
27
+ should "not have file" do
28
+ cleanup
29
+ assert !@dummy.avatar.exists?, "File is still exists, but should not"
30
+ end
31
+ end
32
+
33
+ context "#copy_to_local_file" do
34
+ setup do
35
+ @dummy = Dummy.create!(avatar: @file)
36
+ @dest = File.expand_path(File.join(File.dirname(__FILE__), 'tmp', 'new_file.png'))
37
+ end
38
+
39
+ should "copy to local file" do
40
+ @dummy.avatar.copy_to_local_file(:original, @dest)
41
+ assert File.exist?(@dest), "File was not copied to desired location"
42
+ end
43
+
44
+ should "not copy file if #original_filename is nil" do
45
+ @dummy.avatar.instance_write(:file_name, nil)
46
+ assert !@dummy.avatar.exists?, "File shouldn't be found if avatar_file_name is empty"
47
+ end
48
+
49
+ should "not copy to local file if remote file doesn't exist" do
50
+ @dummy.avatar.copy_to_local_file(:original, @dest)
51
+ cleanup
52
+ assert !File.exist?(@dest), "File shouldn't be found"
53
+ end
54
+ end
55
+
56
+ context "#flush_writes" do
57
+ should "write files to remote files" do
58
+ dummy = Dummy.new(avatar: @file)
59
+ dummy.avatar.flush_writes
60
+ assert File.file?(dummy.avatar.path), "File was not written down"
61
+ end
62
+ end
63
+
64
+ context "#flush_deletes" do
65
+ should "delete files scheduled for deletion" do
66
+ dummy = Dummy.create!(avatar: @file)
67
+ avatar = dummy.avatar.path
68
+ dummy.avatar.send :queue_all_for_delete
69
+ dummy.avatar.flush_deletes
70
+ assert !File.file?(avatar), "File was not deleted"
71
+ end
72
+ end
73
+
74
+ teardown do
75
+ @file.close
76
+ cleanup
77
+ end
78
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: paperclip-sftp
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Yury Velikanau
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: paperclip
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.1.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 3.1.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: net-sftp
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.0.5
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.0.5
46
+ - !ruby/object:Gem::Dependency
47
+ name: minitest
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 3.3.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 3.3.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: minitest_should
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.3.1
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.3.1
78
+ - !ruby/object:Gem::Dependency
79
+ name: sqlite3
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 1.3.3
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 1.3.3
94
+ description: SFTP (Secure File Transfer Protocol) storage for Paperclip.
95
+ email:
96
+ - yury.velikanau@gmail.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - .gitignore
102
+ - Gemfile
103
+ - LICENSE
104
+ - README.md
105
+ - Rakefile
106
+ - lib/paperclip-sftp.rb
107
+ - lib/paperclip-sftp/storage/sftp.rb
108
+ - lib/paperclip-sftp/version.rb
109
+ - paperclip-sftp.gemspec
110
+ - test/fixtures/ruby.png
111
+ - test/integration_test.rb
112
+ - test/test_helper.rb
113
+ - test/unit_test.rb
114
+ homepage: https://github.com/spectator/paperclip-sftp
115
+ licenses: []
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 1.8.24
135
+ signing_key:
136
+ specification_version: 3
137
+ summary: SFTP (Secure File Transfer Protocol) storage for Paperclip.
138
+ test_files:
139
+ - test/fixtures/ruby.png
140
+ - test/integration_test.rb
141
+ - test/test_helper.rb
142
+ - test/unit_test.rb