ftpmock 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,33 @@
1
+ module Ftpmock
2
+ module_function
3
+
4
+ def configure
5
+ yield configuration
6
+ end
7
+
8
+ def configuration
9
+ @configuration ||= Configuration.new
10
+ end
11
+
12
+ class Configuration
13
+ attr_writer :path
14
+ attr_accessor :verbose
15
+
16
+ def initialize(path: nil, verbose: true)
17
+ @path = path
18
+ @verbose = verbose
19
+ end
20
+
21
+ def path
22
+ @path ||= "#{test_dir}/ftp_records"
23
+ end
24
+
25
+ def test_dir
26
+ rspec? ? 'spec' : 'test'
27
+ end
28
+
29
+ def rspec?
30
+ defined?(RSpec)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,46 @@
1
+ module Ftpmock
2
+ module GetHelper
3
+ module_function
4
+
5
+ # reads from cache to localfile
6
+ #
7
+ # true/false
8
+ def read(cache_path, chdir, remotefile, localfile)
9
+ # chdir =
10
+ remotefile = PathHelper.join(PathHelper.simplify(chdir), remotefile).to_s
11
+ # localfile = PathHelper.join(chdir, localfile).to_s
12
+
13
+ cached_path = path_for(cache_path, remotefile)
14
+
15
+ File.exist?(cached_path) && FileUtils.cp(cached_path, localfile)
16
+
17
+ fetched?(localfile)
18
+ end
19
+
20
+ # writes to cache from localfile
21
+ def write(cache_path, chdir, remotefile, localfile)
22
+ return false unless File.exist?(localfile)
23
+
24
+ # chdir =
25
+ remotefile = PathHelper.join(PathHelper.simplify(chdir), remotefile).to_s
26
+ # localfile = PathHelper.join(chdir, localfile).to_s
27
+
28
+ cached_path = path_for(cache_path, remotefile)
29
+ FileUtils.cp(localfile, cached_path)
30
+
31
+ File.exist?(cached_path)
32
+ end
33
+
34
+ def fetched?(localfile)
35
+ File.exist?(localfile)
36
+ end
37
+
38
+ def path_for(cache_path, remotefile)
39
+ path = cache_path.join('get')
40
+ remotefile = PathHelper.simplify(remotefile)
41
+ ret = path.join(remotefile)
42
+ FileUtils.mkdir_p(ret.dirname.to_s)
43
+ ret
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,53 @@
1
+ require 'psych'
2
+
3
+ module Ftpmock
4
+ module ListHelper
5
+ module_function
6
+
7
+ def read(cache_path, chdir, key)
8
+ key = PathHelper.join(chdir, key).to_s if chdir
9
+ dataset = read_dataset(cache_path)
10
+ dataset[key]
11
+ end
12
+
13
+ def write(cache_path, chdir, key, list)
14
+ key = PathHelper.join(chdir, key).to_s if chdir
15
+ dataset = read_dataset(cache_path)
16
+ dataset[key] = list
17
+ write_dataset(cache_path, dataset)
18
+ true
19
+ end
20
+
21
+ def read_dataset(cache_path)
22
+ path = path_for(cache_path)
23
+ string = path.read
24
+ load(string)
25
+ end
26
+
27
+ def write_dataset(cache_path, dataset)
28
+ path = path_for(cache_path)
29
+ content = dump(dataset)
30
+ path.write(content)
31
+ end
32
+
33
+ def path_for(cache_path)
34
+ cache_path = PathHelper.clean(cache_path)
35
+ cache_path.exist? || FileUtils.mkdir_p(cache_path)
36
+ path = PathHelper.clean("#{cache_path}/list.yml")
37
+ path.exist? || path.write(dump(new_list))
38
+ path
39
+ end
40
+
41
+ def new_list
42
+ {}
43
+ end
44
+
45
+ def load(string)
46
+ Psych.load(string)
47
+ end
48
+
49
+ def dump(data)
50
+ Psych.dump(data)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,24 @@
1
+ module Ftpmock
2
+ module PathHelper
3
+ module_function
4
+
5
+ def clean(path)
6
+ path = path.to_s
7
+ path = path[1..-1] if path[0] == '/'
8
+ path = Pathname(path).cleanpath
9
+ return Pathname('') if path.to_s == '.'
10
+
11
+ path
12
+ end
13
+
14
+ def join(path_a, path_b)
15
+ clean Pathname(path_a.to_s).join(path_b.to_s)
16
+ end
17
+
18
+ def simplify(path)
19
+ path.to_s
20
+ .tr('/', '-')
21
+ .tr('..', '__')
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,39 @@
1
+ module Ftpmock
2
+ module PutHelper
3
+ module_function
4
+
5
+ def cached?(cache_path, remotefile)
6
+ path = path_for(cache_path, remotefile)
7
+ path.exist?
8
+ end
9
+
10
+ def exist?(localfile)
11
+ File.exist?(localfile)
12
+ end
13
+
14
+ def write(cache_path, localfile, remotefile)
15
+ path = path_for(cache_path, remotefile)
16
+ FileUtils.cp(localfile, path)
17
+ end
18
+
19
+ # Array
20
+ def compare(cache_path, localfile, remotefile)
21
+ return [] unless cached?(cache_path, remotefile)
22
+
23
+ path = path_for(cache_path, remotefile)
24
+ diff = StringUtils.diff(localfile, path)
25
+ diff.split("\n")
26
+ end
27
+
28
+ # def expire(cache_path, remotefile)
29
+ # path = path_for(cache_path, remotefile)
30
+ # path.exist? && path.delete
31
+ # end
32
+
33
+ def path_for(cache_path, remotefile)
34
+ path = cache_path.join('put')
35
+ FileUtils.mkdir_p(path)
36
+ path.join(remotefile.tr('/', '-'))
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,9 @@
1
+ module Ftpmock
2
+ module MethodMissingMixin
3
+ def method_missing(name, *_args)
4
+ msg = " Missing method `#{self.class.name}##{name}`. Ignoring"
5
+ configuration.verbose && VerboseUtils.warn(msg)
6
+ nil
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,287 @@
1
+ # https://apidock.com/ruby/Net/FTP
2
+ require 'net/ftp'
3
+
4
+ module Ftpmock
5
+ class NetFtpProxy
6
+ # Stubbers
7
+
8
+ Real = begin
9
+ Net::FTP
10
+ rescue NameError
11
+ nil
12
+ end
13
+
14
+ # inspired by https://github.com/bblimke/webmock/blob/master/lib/webmock/http_lib_caches/net_http.rb
15
+ def self.on!
16
+ unless Real
17
+ yield
18
+ return
19
+ end
20
+
21
+ Net.send(:remove_const, :FTP)
22
+ Net.const_set(:FTP, self)
23
+ if block_given?
24
+ yield
25
+ off!
26
+ end
27
+ end
28
+
29
+ def self.off!
30
+ return unless Real
31
+
32
+ Net.send(:remove_const, :FTP)
33
+ Net.const_set(:FTP, Real)
34
+ end
35
+
36
+ PORT = 21
37
+
38
+ # Instance Methods
39
+
40
+ # inspired by https://apidock.com/ruby/v2_6_3/Net/FTP/open/class
41
+ def self.open(host, *args)
42
+ if block_given?
43
+ proxy = new(host, *args)
44
+ begin
45
+ yield proxy
46
+ ensure
47
+ proxy.close
48
+ end
49
+ else
50
+ new(host, *args)
51
+ end
52
+ end
53
+
54
+ # inspired by https://apidock.com/ruby/v2_6_3/Net/FTP/new/class
55
+ def initialize(host = nil, *args)
56
+ @options = args.last.is_a?(Hash) ? args.pop : {}
57
+ @host = host
58
+ @configuration = @options[:configuration] || Ftpmock.configuration
59
+
60
+ if args.size == 2
61
+ @options[:username] ||= args[0]
62
+ @options[:password] ||= args[1]
63
+ end
64
+
65
+ if host
66
+ connect(host, @options[:port] || PORT)
67
+ login(@options[:username], @options[:password]) if @options[:username]
68
+ end
69
+ end
70
+
71
+ def real
72
+ @real ||= Real.new
73
+ end
74
+
75
+ attr_writer :cache, :real
76
+ attr_reader :configuration,
77
+ :host,
78
+ :port,
79
+ :username,
80
+ :password
81
+
82
+ # connection methods
83
+
84
+ def connect(host, port = PORT)
85
+ @real_connected = false
86
+ @host = host
87
+ @port = port
88
+
89
+ StringUtils.all_present?(host, port) || _raise_not_connected
90
+
91
+ @cache_connected = true
92
+ true
93
+ end
94
+
95
+ def login(username, password)
96
+ @cache = nil
97
+ @real_logged = false
98
+ @username = username
99
+ @password = password
100
+
101
+ _init_cache
102
+
103
+ @cache_logged = true
104
+ true
105
+ end
106
+
107
+ def _real_connect_and_login
108
+ @cache_connected || _raise_not_connected
109
+ @cache_logged || _raise_not_connected
110
+
111
+ @real_connected || real.connect(*_real_connect_args)
112
+ @real_connected = true
113
+
114
+ @real_logged || real.login(*_real_login_args)
115
+ @real_logged = true
116
+
117
+ if @chdir
118
+ @real.chdir(@chdir)
119
+ @chdir = nil
120
+ end
121
+ end
122
+
123
+ def _real_connect_args
124
+ [host, port]
125
+ end
126
+
127
+ def _real_login_args
128
+ [username, password].select { |string| StringUtils.present?(string) }
129
+ end
130
+
131
+ def _init_cache
132
+ credentials = [host, port, username, password]
133
+ @cache = Cache.new(configuration, credentials)
134
+ end
135
+
136
+ def cache
137
+ @cache || _raise_not_connected
138
+ end
139
+
140
+ def _raise_not_connected
141
+ raise(Net::FTPConnectionError, 'not connected')
142
+ end
143
+
144
+ # directory methods
145
+
146
+ # https://docs.ruby-lang.org/en/2.0.0/Net/FTP.html#method-i-chdir
147
+ def chdir(dirname = nil)
148
+ cache.chdir(dirname)
149
+ end
150
+
151
+ def pwd
152
+ chdir
153
+ end
154
+
155
+ def getdir
156
+ chdir
157
+ end
158
+
159
+ # list methods
160
+
161
+ def list(*args)
162
+ cache.list(*args) do
163
+ _real_connect_and_login
164
+ real.list(*args)
165
+ end
166
+ end
167
+
168
+ # get methods
169
+
170
+ def get(remotefile, localfile = File.basename(remotefile))
171
+ cache.get(remotefile, localfile) do
172
+ _real_connect_and_login
173
+ real.get(remotefile, localfile)
174
+ end
175
+
176
+ true
177
+ end
178
+
179
+ def gettextfile(remotefile, localfile = File.basename(remotefile))
180
+ cache.get(remotefile, localfile) do
181
+ _real_connect_and_login
182
+ real.gettextfile(remotefile, localfile)
183
+ end
184
+
185
+ true
186
+ end
187
+
188
+ def getbinaryfile(remotefile, localfile = File.basename(remotefile))
189
+ cache.get(remotefile, localfile) do
190
+ _real_connect_and_login
191
+ real.getbinaryfile(remotefile, localfile)
192
+ end
193
+
194
+ true
195
+ end
196
+
197
+ # put methods
198
+
199
+ # TODO: block
200
+ # https://docs.ruby-lang.org/en/2.0.0/Net/FTP.html#method-i-put
201
+ def put(localfile, remotefile = File.basename(localfile))
202
+ cache.put(localfile, remotefile) do
203
+ _real_connect_and_login
204
+ real.put(localfile, remotefile)
205
+ end
206
+
207
+ true
208
+ end
209
+
210
+ # TODO: block
211
+ def puttextfile(localfile, remotefile = File.basename(localfile))
212
+ cache.put(localfile, remotefile) do
213
+ _real_connect_and_login
214
+ real.puttextfile(localfile, remotefile)
215
+ end
216
+
217
+ true
218
+ end
219
+
220
+ # TODO: block
221
+ def putbinaryfile(localfile, remotefile = File.basename(localfile))
222
+ cache.put(localfile, remotefile) do
223
+ _real_connect_and_login
224
+ real.putbinaryfile(localfile, remotefile)
225
+ end
226
+
227
+ true
228
+ end
229
+
230
+ # TODO: Methods Not Implemented
231
+
232
+ # abort
233
+ # acct
234
+ # binary
235
+ # binary=
236
+ # close
237
+ # closed?
238
+ # debug_mode
239
+ # debug_mode=
240
+ # delete
241
+ # help
242
+ # last_response
243
+ # last_response_code
244
+ # mdtm
245
+ # mkdir
246
+ # mlsd
247
+ # mlst
248
+ # mon_enter
249
+ # mon_exit
250
+ # mon_locked?
251
+ # mon_owned?
252
+ # mon_synchronize
253
+ # mon_try_enter
254
+ # mtime
255
+ # new_cond
256
+ # nlst
257
+ # noop
258
+ # open_timeout
259
+ # open_timeout=
260
+ # passive
261
+ # passive=
262
+ # quit
263
+ # read_timeout
264
+ # read_timeout=
265
+ # rename
266
+ # resume
267
+ # resume=
268
+ # retrbinary
269
+ # retrlines
270
+ # return_code
271
+ # return_code=
272
+ # rmdir
273
+ # sendcmd
274
+ # set_socket
275
+ # site
276
+ # size
277
+ # ssl_handshake_timeout
278
+ # ssl_handshake_timeout=
279
+ # status
280
+ # storbinary
281
+ # storlines
282
+ # system
283
+ # voidcmd
284
+ # welcome
285
+ include MethodMissingMixin
286
+ end
287
+ end