paperclipftp 0.1.0 → 0.2.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 (5) hide show
  1. data/LICENSE +1 -1
  2. data/README.rdoc +48 -17
  3. data/VERSION +1 -1
  4. data/lib/paperclipftp.rb +165 -91
  5. metadata +16 -7
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Damian Caruso
1
+ Copyright (c) 2010 Damian Caruso
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = PaperclipFTP
2
2
 
3
- Ftp storage support for paperclip file attachment plugin.
3
+ Ftp storage support for paperclip file attachment plugin.
4
4
  Useful for CDNs with ftp access method for file uploading.
5
5
 
6
6
  == Install
@@ -10,30 +10,61 @@ Useful for CDNs with ftp access method for file uploading.
10
10
 
11
11
  == Usage
12
12
 
13
- Create the file config/paperclipftp.yml:
13
+ In your model:
14
14
 
15
- development:
16
- host: domain.com
17
- username: user
18
- password: password
19
-
20
- test:
21
- ...
22
-
23
- production:
24
- ...
15
+ class User < ActiveRecord::Base
16
+ has_attached_file :avatar,
17
+ :styles => { :medium => "300x300>", :thumb => "100x100>" },
18
+ :path => "/path-to-images/:attachment/:id/:style/:filename",
19
+ :url => "http://my-cdn-domain.com/:attachment/:id/:style/:filename",
20
+ :storage => :ftp,
21
+ :ftp_credentials => Rails.root.join('config', 'paperclipftp.yaml')
22
+ end
25
23
 
26
- In your model:
24
+ or
27
25
 
28
26
  class User < ActiveRecord::Base
29
27
  has_attached_file :avatar,
30
28
  :styles => { :medium => "300x300>", :thumb => "100x100>" },
29
+ :path => "/path-to-images/:attachment/:id/:style/:filename",
30
+ :url => "http://my-cdn-domain.com/:attachment/:id/:style/:filename",
31
31
  :storage => :ftp,
32
- :path => "/:attachment/:attachment/:id/:style/:filename"
33
- :url => "http://domain.com/:attachment/:attachment/:id/:style/:filename"
32
+ :ftp_credentials => { :host => 'ftp.domain.com', :username => 'username', :password => 'password' },
33
+ :ftp_passive_mode => false,
34
+ :ftp_timeout => 90,
35
+ :ftp_verify_size_on_upload => false,
36
+ :ftp_debug_mode => false
34
37
  end
35
-
36
- You must add in the url option the web access to that file.
38
+
39
+ Options storage, ftp_credentials, path and url are mandatory.
40
+
41
+ * +storage+: Should be set to 'ftp' value in order to utilize paperclipftp plugin
42
+ * +ftp_credentials+: Takes a path, a File, or a Hash. The path (or File) must point
43
+ to a YAML file containing the +host+, +username+, and +password+ to ftp server.
44
+ You can 'environment-space' this just like you do to your
45
+ database.yml file, so different environments can use different accounts:
46
+ development:
47
+ host: domain.com
48
+ username: user
49
+ password: password
50
+ test:
51
+ ...
52
+ production:
53
+ ...
54
+ This is not required, however, and the file may simply look like this:
55
+ host: domain.com
56
+ username: user
57
+ password: password
58
+ In which case, those values will be used in all environments.
59
+ * +path+: This is the templated path to the directory on the ftp server in which the file will be stored.
60
+ * +url+: This is the templated url to the files as accessed from CDN.
61
+
62
+ Other options are optional.
63
+
64
+ * +ftp_passive_mode+: True if ftp communication should happen in passive mode
65
+ * +ftp_timeout+: Max amount of time in seconds to allocate to ftp operations of the plugin
66
+ * +ftp_debug_mode+: If true, dumps copy of all ftp commands to stderr
67
+ * +ftp_verify_size_on_upload+: If true, file size will be verified after each upload
37
68
 
38
69
  == Copyright
39
70
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
data/lib/paperclipftp.rb CHANGED
@@ -1,91 +1,165 @@
1
- module Paperclip
2
- module Storage
3
- module Ftp
4
- def self.extended base
5
- require 'net/ftp'
6
- base.instance_eval do
7
- @ftp_credentials = parse_credentials
8
- end
9
- end
10
-
11
- def ftp
12
- if @ftp.nil? || @ftp.closed?
13
- @ftp = Net::FTP.new(@ftp_credentials[:host], @ftp_credentials[:username], @ftp_credentials[:password])
14
- end
15
- @ftp
16
- end
17
-
18
- def exists?(style = default_style)
19
- move_to_remote_path(File.dirname(path(style)))
20
- ftp.size(File.basename(path(style))) > 0 ? true : false
21
- rescue Net::FTPPermError => e
22
- #File not exists
23
- false
24
- rescue Net::FTPReplyError => e
25
- ftp.close
26
- raise e
27
- end
28
-
29
- def to_file style = default_style
30
- @queued_for_write[style] || "ftp://#{path(style)}"
31
- end
32
- alias_method :to_io, :to_file
33
-
34
- def flush_writes
35
- @queued_for_write.each do |style, file|
36
- file.close
37
- move_to_remote_path(File.dirname(path(style)))
38
- log("uploading #{path(style)}")
39
- ftp.putbinaryfile(file.path, File.basename(path(style)))
40
- end
41
- @queued_for_write = {}
42
- rescue Net::FTPReplyError => e
43
- raise e
44
- rescue Net::FTPPermError => e
45
- raise e
46
- ensure
47
- ftp.close
48
- end
49
-
50
- def flush_deletes
51
- @queued_for_delete.each do |path|
52
- begin
53
- move_to_remote_path(File.dirname(path))
54
- log("deleting #{path}")
55
- ftp.delete(File.basename(path))
56
- rescue Net::FTPPermError, Net::FTPReplyError
57
- end
58
- end
59
- @queued_for_delete = []
60
- rescue Net::FTPReplyError => e
61
- raise e
62
- rescue Net::FTPPermError => e
63
- raise e
64
- ensure
65
- ftp.close
66
- end
67
-
68
- def move_to_remote_path(rpath)
69
- ftp.chdir("/")
70
- rpath.split(File::SEPARATOR).each do |rdir|
71
- rdir = rdir.strip
72
- unless rdir.blank?
73
- list = ftp.ls.collect { |f| f.split.last }
74
- unless list.include?(rdir)
75
- ftp.mkdir(rdir)
76
- end
77
- ftp.chdir(rdir)
78
- end
79
- end
80
- end
81
-
82
- def parse_credentials
83
- creds = YAML.load_file(File.join(RAILS_ROOT,"config","paperclipftp.yml"))
84
- creds = creds.stringify_keys
85
- (creds[RAILS_ENV] || creds).symbolize_keys
86
- end
87
- private :parse_credentials
88
-
89
- end
90
- end
91
- end
1
+ module Paperclip
2
+ module Storage
3
+ module Ftp
4
+ class FtpTimeout < Timeout::Error; end
5
+
6
+ def self.extended base
7
+ require 'net/ftp'
8
+ base.instance_eval do
9
+ @ftp_credentials = parse_credentials(@options[:ftp_credentials])
10
+ @passive_mode = !!@options[:ftp_passive_mode]
11
+ @debug_mode = !!@options[:ftp_debug_mode]
12
+ @verify_size = !!@options[:ftp_verify_size_on_upload]
13
+ @timeout = @options[:ftp_timeout] || 3600
14
+ end
15
+ end
16
+
17
+ def exists?(style = default_style)
18
+ Timeout::timeout(@timeout, FtpTimeout) do
19
+ file_size(ftp_path(style)) > 0
20
+ end
21
+ end
22
+
23
+ def to_file style = default_style
24
+ return @queued_for_write[style] if @queued_for_write[style]
25
+ Timeout::timeout(@timeout, FtpTimeout) do
26
+ file = Tempfile.new(ftp_path(style))
27
+ ftp.getbinaryfile(ftp_path(style), file.path)
28
+ file.rewind
29
+ return file
30
+ end
31
+ end
32
+
33
+ alias_method :to_io, :to_file
34
+
35
+ def flush_writes
36
+ @queued_for_write.each do |style, file|
37
+ Timeout::timeout(@timeout, FtpTimeout) do
38
+ file.close
39
+ remote_path = ftp_path(style)
40
+ log("uploading #{remote_path}")
41
+ first_try = true
42
+ begin
43
+ ftp.putbinaryfile(file.path, remote_path)
44
+ rescue Net::FTPPermError => e
45
+ if first_try
46
+ first_try = false
47
+ create_parent_folder_for(remote_path)
48
+ retry
49
+ else
50
+ raise e
51
+ end
52
+ end
53
+ if @verify_size
54
+ # avoiding those weird occasional 0 file sizes by not using instance method file.size
55
+ local_file_size = File.size(file.path)
56
+ remote_file_size = file_size(remote_path)
57
+ raise Net::FTPError.new "Uploaded #{remote_file_size} bytes instead of #{local_file_size} bytes" unless remote_file_size == local_file_size
58
+ end
59
+ end
60
+ end
61
+ @queued_for_write = {}
62
+ rescue Net::FTPReplyError => e
63
+ raise e
64
+ rescue Net::FTPPermError => e
65
+ raise e
66
+ ensure
67
+ close_ftp_connection
68
+ end
69
+
70
+ def flush_deletes
71
+ @queued_for_delete.each do |path|
72
+ Timeout::timeout(@timeout, FtpTimeout) do
73
+ begin
74
+ log("deleting #{path}")
75
+ ftp.delete('/' + path)
76
+ rescue Net::FTPPermError, Net::FTPReplyError
77
+ end
78
+ end
79
+ end
80
+ @queued_for_delete = []
81
+ rescue Net::FTPReplyError => e
82
+ raise e
83
+ rescue Net::FTPPermError => e
84
+ raise e
85
+ ensure
86
+ close_ftp_connection
87
+ end
88
+
89
+ private
90
+
91
+ def ftp
92
+ if @ftp.nil? || @ftp.closed?
93
+ Timeout::timeout(@timeout, FtpTimeout) do
94
+ @ftp = Net::FTP.new(@ftp_credentials[:host], @ftp_credentials[:username], @ftp_credentials[:password])
95
+ @ftp.debug_mode = @debug_mode
96
+ @ftp.passive = @passive_mode
97
+ end
98
+ end
99
+ @ftp
100
+ end
101
+
102
+ def close_ftp_connection
103
+ unless @ftp.nil? || @ftp.closed?
104
+ @ftp.close
105
+ @ftp = nil
106
+ end
107
+ end
108
+
109
+ def create_parent_folder_for(remote_path)
110
+ dir_path = File.dirname(remote_path)
111
+ ftp.chdir("/")
112
+ dir_path.split(File::SEPARATOR).each do |rdir|
113
+ next if rdir.blank?
114
+ first_time = true
115
+ begin
116
+ ftp.chdir(rdir)
117
+ rescue Net::FTPPermError => e
118
+ if first_time
119
+ ftp.mkdir(rdir)
120
+ first_time = false
121
+ retry
122
+ else
123
+ raise e
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ def ftp_path(style)
130
+ path = path(style)
131
+ path.nil? ? nil : '/' + path
132
+ end
133
+
134
+ def file_size(remote_path)
135
+ ftp.size(remote_path)
136
+ rescue Net::FTPPermError => e
137
+ #File not exists
138
+ -1
139
+ rescue Net::FTPReplyError => e
140
+ close_ftp_connection
141
+ raise e
142
+ end
143
+
144
+ def parse_credentials creds
145
+ creds = find_credentials(creds).stringify_keys
146
+ (creds[Rails.env] || creds).symbolize_keys
147
+ end
148
+
149
+ def find_credentials creds
150
+ case creds
151
+ when File
152
+ YAML::load(ERB.new(File.read(creds.path)).result)
153
+ when String, Pathname
154
+ YAML::load(ERB.new(File.read(creds)).result)
155
+ when Hash
156
+ creds
157
+ else
158
+ raise ArgumentError, "Credentials are not a path, file, or hash."
159
+ end
160
+ end
161
+
162
+
163
+ end
164
+ end
165
+ end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paperclipftp
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 23
5
+ prerelease:
5
6
  segments:
6
7
  - 0
7
- - 1
8
+ - 2
8
9
  - 0
9
- version: 0.1.0
10
+ version: 0.2.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Damian Caruso
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-03-08 00:00:00 -03:00
18
+ date: 2011-05-13 00:00:00 -04:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: paperclip
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 3
27
30
  segments:
28
31
  - 2
29
32
  - 3
@@ -35,9 +38,11 @@ dependencies:
35
38
  name: yard
36
39
  prerelease: false
37
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
38
42
  requirements:
39
43
  - - ">="
40
44
  - !ruby/object:Gem::Version
45
+ hash: 3
41
46
  segments:
42
47
  - 0
43
48
  version: "0"
@@ -65,28 +70,32 @@ homepage: http://github.com/cdamian/paperclipftp
65
70
  licenses: []
66
71
 
67
72
  post_install_message:
68
- rdoc_options:
69
- - --charset=UTF-8
73
+ rdoc_options: []
74
+
70
75
  require_paths:
71
76
  - lib
72
77
  required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
73
79
  requirements:
74
80
  - - ">="
75
81
  - !ruby/object:Gem::Version
82
+ hash: 3
76
83
  segments:
77
84
  - 0
78
85
  version: "0"
79
86
  required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
80
88
  requirements:
81
89
  - - ">="
82
90
  - !ruby/object:Gem::Version
91
+ hash: 3
83
92
  segments:
84
93
  - 0
85
94
  version: "0"
86
95
  requirements: []
87
96
 
88
97
  rubyforge_project:
89
- rubygems_version: 1.3.6
98
+ rubygems_version: 1.6.2
90
99
  signing_key:
91
100
  specification_version: 3
92
101
  summary: Ftp storage support for paperclip file attachment