rest-ftp-daemon 0.6.0 → 0.9.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/Gemfile.lock +2 -2
- data/README.md +87 -57
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/rest-ftp-daemon +3 -2
- data/lib/config.rb +0 -4
- data/lib/errors.rb +1 -5
- data/lib/extend_threads.rb +14 -0
- data/lib/rest-ftp-daemon.rb +117 -71
- data/rest-ftp-daemon.gemspec +4 -3
- metadata +6 -4
data/Gemfile.lock
CHANGED
@@ -13,7 +13,7 @@ GEM
|
|
13
13
|
thread_safe (~> 0.3, >= 0.3.1)
|
14
14
|
faraday (0.9.0)
|
15
15
|
multipart-post (>= 1.2, < 3)
|
16
|
-
git (1.2.
|
16
|
+
git (1.2.8)
|
17
17
|
github_api (0.12.0)
|
18
18
|
addressable (~> 2.3)
|
19
19
|
descendants_tracker (~> 0.0.4)
|
@@ -53,7 +53,7 @@ GEM
|
|
53
53
|
rack-protection (1.5.3)
|
54
54
|
rack
|
55
55
|
rake (10.3.2)
|
56
|
-
rdoc (
|
56
|
+
rdoc (4.1.1)
|
57
57
|
json (~> 1.4)
|
58
58
|
shoulda (3.5.0)
|
59
59
|
shoulda-context (~> 1.0, >= 1.0.1)
|
data/README.md
CHANGED
@@ -1,90 +1,120 @@
|
|
1
|
-
|
1
|
+
rest-ftp-daemon
|
2
|
+
====================================================================================
|
3
|
+
|
4
|
+
This is a pretty simple FTP client daemon, controlled through a RESTfull API.
|
2
5
|
|
3
|
-
This is a fairly basic FTP client daemon, driven by REST-based webservice calls.
|
4
6
|
As of today, its main features are :
|
5
7
|
|
6
|
-
*
|
7
|
-
* Spawn a dedicated thread to handle this job in its own
|
8
|
-
* Report transfer
|
9
|
-
*
|
10
|
-
* Report JSON status of workers on GET /jobs/ for automated monitoring
|
8
|
+
* Delegate a transfer job, ```PUT```'ing posting simple JSON structure
|
9
|
+
* Spawn a dedicated thread to handle this job in its own context
|
10
|
+
* Report transfer status, progress and errors for each delegated job
|
11
|
+
* Expose JSON status of workers on ```GET /jobs/``` for automated monitoring
|
11
12
|
|
12
13
|
|
13
|
-
|
14
|
+
Installation
|
15
|
+
------------------------------------------------------------------------------------
|
14
16
|
|
15
|
-
This project requires ruby >= 1.9 and rubygems installed.
|
17
|
+
This project is available as a rubygem, requires on ruby >= 1.9 and rubygems installed.
|
16
18
|
|
17
|
-
|
19
|
+
Get and install the gem from rubygems.org:
|
18
20
|
|
19
|
-
|
21
|
+
```
|
22
|
+
gem install rest-ftp-daemon
|
23
|
+
```
|
20
24
|
|
21
25
|
Start the daemon:
|
22
26
|
|
23
|
-
```
|
27
|
+
```
|
28
|
+
rest-ftp-daemon start
|
29
|
+
```
|
30
|
+
|
31
|
+
Check that the daemon is running and giving status info
|
32
|
+
|
33
|
+
```
|
34
|
+
http://localhost:3000/
|
35
|
+
```
|
24
36
|
|
25
|
-
For now, daemon logs to APP_LOGTO defined in lib/config.rb
|
37
|
+
For now, daemon logs to ```APP_LOGTO``` defined in ```lib/config.rb```
|
26
38
|
|
27
39
|
|
28
|
-
|
40
|
+
Usage examples
|
41
|
+
------------------------------------------------------------------------------------
|
29
42
|
|
30
|
-
|
43
|
+
Start a job to transfer a file named "file.ova" to a local FTP server
|
31
44
|
|
32
|
-
|
33
|
-
|
45
|
+
```
|
46
|
+
curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
|
47
|
+
'{"source":"~/file.ova","target":"ftp://anonymous@localhost/incoming/dest2.ova"}' "http://localhost:3000/jobs"
|
48
|
+
```
|
34
49
|
|
50
|
+
Start a job to transfer a file named "file.dmg" to a local FTP server
|
35
51
|
|
36
|
-
|
52
|
+
```
|
53
|
+
curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
|
54
|
+
'{"source":"~/file.dmg","target":"ftp://anonymous@localhost/incoming/dest4.dmg"}' "http://localhost:3000/jobs"
|
55
|
+
```
|
37
56
|
|
38
|
-
|
39
|
-
'{"source":"~/file.dmg","target":"ftp://anonymous@localhost/incoming/dest4.dmg"}' "http://localhost:3000/jobs"
|
57
|
+
Get status of a specific job based on its name
|
40
58
|
|
59
|
+
```
|
60
|
+
curl -H "Content-Type: application/json" -X DELETE -D /dev/stdout "http://localhost:3000/jobs/bob-45320-1"
|
61
|
+
```
|
41
62
|
|
42
|
-
Delete a specific job
|
63
|
+
Delete a specific job based on its name
|
43
64
|
|
44
|
-
|
65
|
+
```
|
66
|
+
curl -H "Content-Type: application/json" -X DELETE -D /dev/stdout "http://localhost:3000/jobs/bob-45320-1"
|
67
|
+
```
|
45
68
|
|
46
69
|
|
70
|
+
Getting status
|
71
|
+
------------------------------------------------------------------------------------
|
47
72
|
|
48
|
-
|
73
|
+
The server exposes jobs list on ``` GET /jobs ```
|
49
74
|
|
50
|
-
|
75
|
+
```
|
76
|
+
http://localhost:3000/jobs
|
77
|
+
```
|
51
78
|
|
52
|
-
|
79
|
+
This query will return a job list :
|
53
80
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
81
|
+
```
|
82
|
+
[
|
83
|
+
{
|
84
|
+
"source": "~/file.dmg",
|
85
|
+
"target": "ftp://anonymous@localhost/incoming/dest2.dmg",
|
86
|
+
"worker_name": "bob-92439-1",
|
87
|
+
"created": "2014-08-01 16:53:08 +0200",
|
88
|
+
"id": 16,
|
89
|
+
"runtime": 17.4,
|
90
|
+
"status": "graceful_ending",
|
91
|
+
"source_size": 37109074,
|
92
|
+
"error": 0,
|
93
|
+
"errmsg": "finished",
|
94
|
+
"progress": 100.0,
|
95
|
+
"transferred": 37100000
|
96
|
+
},
|
97
|
+
{
|
98
|
+
"source": "~/file.ova",
|
99
|
+
"target": "ftp://anonymous@localhost/incoming/dest2.ova",
|
100
|
+
"worker_name": "bob-92439-2",
|
101
|
+
"created": "2014-08-01 16:53:12 +0200",
|
102
|
+
"id": 17,
|
103
|
+
"runtime": 13.8,
|
104
|
+
"status": "uploading",
|
105
|
+
"source_size": 1849036800,
|
106
|
+
"error": -1,
|
107
|
+
"errmsg": "uploading",
|
108
|
+
"progress": 36.1,
|
109
|
+
"transferred": 668300000
|
110
|
+
}
|
111
|
+
]
|
112
|
+
```
|
84
113
|
|
85
114
|
|
86
|
-
|
115
|
+
About
|
116
|
+
------------------------------------------------------------------------------------
|
87
117
|
|
88
|
-
Bruno MEDICI
|
118
|
+
Bruno MEDICI Consultant
|
89
119
|
|
90
120
|
http://bmconseil.com/
|
data/Rakefile
CHANGED
@@ -18,7 +18,7 @@ Jeweler::Tasks.new do |gem|
|
|
18
18
|
gem.homepage = "http://github.com/bmedici/rest-ftp-daemon"
|
19
19
|
gem.license = "MIT"
|
20
20
|
gem.summary = "RESTful FTP client daemon"
|
21
|
-
gem.description = "
|
21
|
+
gem.description = "This is a pretty simple FTP client daemon, controlled through a RESTfull API"
|
22
22
|
gem.email = "rest-ftp-daemon@bmconseil.com"
|
23
23
|
gem.authors = ["Bruno"]
|
24
24
|
# dependencies defined in Gemfile
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.9.0
|
data/bin/rest-ftp-daemon
CHANGED
@@ -5,9 +5,11 @@ require 'thin'
|
|
5
5
|
|
6
6
|
# Initialize some local constants
|
7
7
|
APP_ROOT = File.dirname(__FILE__) + '/../'
|
8
|
+
APP_NAME = 'rest-ftp-daemon'
|
8
9
|
APP_VER = File.read "#{APP_ROOT}/VERSION"
|
9
10
|
APP_STARTED = Time.now
|
10
11
|
APP_DEFAULT_PORT = 3000
|
12
|
+
APP_LOGTO = "/tmp/#{APP_NAME}.log"
|
11
13
|
|
12
14
|
# Prepare thin
|
13
15
|
rackup_file = File.expand_path "#{APP_ROOT}/lib/config.ru"
|
@@ -15,7 +17,6 @@ argv = ARGV
|
|
15
17
|
argv << ["-R", rackup_file] unless ARGV.include?("-R")
|
16
18
|
argv << ["-p", APP_DEFAULT_PORT.to_s] unless ARGV.include?("-p")
|
17
19
|
argv << ["-e", "production"] unless ARGV.include?("-e")
|
18
|
-
argv << ["
|
20
|
+
argv << ["--stats", "/stats"] unless ARGV.include?("--stats")
|
19
21
|
|
20
|
-
# Start thin
|
21
22
|
Thin::Runner.new(argv.flatten).run!
|
data/lib/config.rb
CHANGED
data/lib/errors.rb
CHANGED
@@ -1,8 +1,3 @@
|
|
1
|
-
# Error codes
|
2
|
-
# O: all ok
|
3
|
-
# 1x: request body errors
|
4
|
-
# 2x: transfer errors
|
5
|
-
|
6
1
|
ERR_OK = 0
|
7
2
|
ERR_BUSY = -1
|
8
3
|
|
@@ -13,4 +8,5 @@ ERR_REQ_TARGET_SCHEME = 13
|
|
13
8
|
ERR_JOB_SOURCE_NOTFOUND = 21
|
14
9
|
ERR_JOB_TARGET_UNPARSEABLE = 22
|
15
10
|
ERR_JOB_PERMISSION = 24
|
11
|
+
ERR_JOB_TARGET_PRESENT = 25
|
16
12
|
|
data/lib/rest-ftp-daemon.rb
CHANGED
@@ -4,18 +4,22 @@ class RestFtpDaemon < Sinatra::Base
|
|
4
4
|
configure :development, :production do
|
5
5
|
|
6
6
|
# Create new thread group
|
7
|
-
@@
|
7
|
+
@@workers = ThreadGroup.new
|
8
|
+
|
9
|
+
# Logging configuration
|
10
|
+
#use Rack::CommonLogger, logger
|
8
11
|
|
9
12
|
# Some other configuration
|
10
|
-
|
11
|
-
|
13
|
+
disable :sessions
|
14
|
+
disable :logging
|
12
15
|
end
|
13
16
|
|
14
17
|
# Server initialization
|
15
18
|
def initialize
|
16
19
|
# Setup logger
|
17
20
|
@logger = Logger.new(APP_LOGTO, 'daily')
|
18
|
-
|
21
|
+
#@logger = Logger.new
|
22
|
+
#@logger.level = Logger::INFO
|
19
23
|
|
20
24
|
# Other stuff
|
21
25
|
@@last_worker_id = 0
|
@@ -42,23 +46,34 @@ class RestFtpDaemon < Sinatra::Base
|
|
42
46
|
# Build response
|
43
47
|
content_type :json
|
44
48
|
JSON.pretty_generate get_jobs
|
45
|
-
#@@threads.count
|
46
49
|
end
|
47
50
|
|
48
|
-
#
|
49
|
-
|
51
|
+
# Get job info
|
52
|
+
get "/jobs/:id" do
|
50
53
|
# Debug query
|
51
|
-
info "
|
54
|
+
info "GET /jobs/#{params[:id]}"
|
52
55
|
|
53
|
-
#
|
54
|
-
|
56
|
+
# Find this process by name
|
57
|
+
found = find_job params[:id]
|
55
58
|
|
56
|
-
#
|
57
|
-
error 404 if
|
59
|
+
# Build response
|
60
|
+
error 404 and return if found.nil?
|
61
|
+
content_type :json
|
62
|
+
JSON.pretty_generate found
|
63
|
+
end
|
64
|
+
|
65
|
+
# Delete jobs
|
66
|
+
delete "/jobs/:id" do
|
67
|
+
# Debug query
|
68
|
+
info "DELETE /jobs/#{params[:name]}"
|
69
|
+
|
70
|
+
# Find and kill this job
|
71
|
+
found = delete_job params[:id]
|
58
72
|
|
59
73
|
# Build response
|
74
|
+
error 404 and return if found.nil?
|
60
75
|
content_type :json
|
61
|
-
JSON.pretty_generate
|
76
|
+
JSON.pretty_generate found
|
62
77
|
end
|
63
78
|
|
64
79
|
# Spawn a new thread for this new job
|
@@ -83,23 +98,25 @@ class RestFtpDaemon < Sinatra::Base
|
|
83
98
|
def process_job
|
84
99
|
# Init
|
85
100
|
info "process_job: starting"
|
86
|
-
|
101
|
+
job = Thread.current.job
|
102
|
+
job_status :started
|
87
103
|
transferred = 0
|
88
104
|
|
89
105
|
# Check source
|
90
|
-
job_source = File.expand_path(
|
106
|
+
job_source = File.expand_path(job["source"])
|
91
107
|
if !(File.exists? job_source)
|
92
|
-
|
108
|
+
job_error ERR_JOB_SOURCE_NOTFOUND, :ERR_JOB_SOURCE_NOTFOUND
|
93
109
|
return
|
94
110
|
end
|
95
111
|
info "process_job: job_source: #{job_source}"
|
96
112
|
source_size = File.size job_source
|
113
|
+
job_set :source_size, source_size
|
97
114
|
|
98
115
|
# Check target
|
99
|
-
job_target =
|
116
|
+
job_target = job["target"]
|
100
117
|
target = URI(job_target) rescue nil
|
101
118
|
if job_target.nil? || target.nil?
|
102
|
-
|
119
|
+
job_error ERR_JOB_TARGET_UNPARSEABLE, :ERR_JOB_TARGET_UNPARSEABLE
|
103
120
|
return
|
104
121
|
end
|
105
122
|
info "process_job: job_target: #{job_target}"
|
@@ -117,77 +134,102 @@ class RestFtpDaemon < Sinatra::Base
|
|
117
134
|
ftp.login
|
118
135
|
ftp.chdir(target_path)
|
119
136
|
|
137
|
+
|
138
|
+
# Check if target file is found
|
139
|
+
info "source: checking target file"
|
140
|
+
job_status :checking_target
|
141
|
+
job_error ERR_BUSY, :checking_target
|
142
|
+
|
143
|
+
results = ftp.list(target_name)
|
144
|
+
info "ftp.list: #{results}"
|
145
|
+
unless results.count.zero?
|
146
|
+
job_error ERR_JOB_TARGET_PRESENT, :ERR_JOB_TARGET_PRESENT
|
147
|
+
info "target: existing: ERR_JOB_TARGET_PRESENT"
|
148
|
+
ftp.close
|
149
|
+
return
|
150
|
+
end
|
151
|
+
|
152
|
+
|
120
153
|
# Do transfer
|
121
154
|
info "source: starting stransfer"
|
122
|
-
Thread.current[:status] = :transferring
|
123
|
-
job_status
|
124
|
-
|
155
|
+
#Thread.current[:status] = :transferring
|
156
|
+
job_status :uploading
|
157
|
+
job_error ERR_BUSY, :uploading
|
125
158
|
|
126
159
|
begin
|
127
160
|
ftp.putbinaryfile(job_source, target_name, TRANSFER_CHUNK_SIZE) do |block|
|
128
161
|
# Update thread info
|
129
162
|
percent = (100.0 * transferred / source_size).round(1)
|
130
|
-
info "transferring [#{percent} %] of [#{target_name}]"
|
131
163
|
job_set :progress, percent
|
132
164
|
job_set :transferred, transferred
|
165
|
+
info "transferring [#{percent} %] of [#{target_name}]"
|
133
166
|
|
134
167
|
# Update counters
|
135
168
|
transferred += TRANSFER_CHUNK_SIZE
|
136
169
|
end
|
137
170
|
|
138
171
|
rescue Net::FTPPermError
|
139
|
-
|
140
|
-
|
172
|
+
#job_status :failed
|
173
|
+
job_error ERR_JOB_PERMISSION, :ERR_JOB_PERMISSION
|
141
174
|
info "source: FAILED: PERMISSIONS ERROR"
|
142
175
|
|
143
176
|
else
|
144
|
-
|
145
|
-
|
177
|
+
#job_status :finished
|
178
|
+
job_error ERR_OK, :finished
|
146
179
|
info "source: finished stransfer"
|
147
180
|
end
|
148
181
|
|
182
|
+
# Close FTP connexion
|
149
183
|
ftp.close
|
150
184
|
end
|
151
185
|
|
152
186
|
def get_status
|
153
187
|
info "> get_status"
|
154
|
-
|
155
188
|
{
|
156
189
|
app_name: APP_NAME,
|
157
190
|
hostname: @@hostname,
|
158
191
|
version: APP_VER,
|
159
192
|
started: APP_STARTED,
|
160
193
|
uptime: (Time.now - APP_STARTED).round(1),
|
161
|
-
jobs_count: @@
|
194
|
+
jobs_count: @@workers.list.count,
|
162
195
|
}
|
163
196
|
end
|
164
197
|
|
165
198
|
def get_jobs
|
166
199
|
info "> get_jobs"
|
167
200
|
|
168
|
-
|
169
|
-
@@
|
170
|
-
output << {
|
171
|
-
:id => thread[:name],
|
172
|
-
:process => thread.status,
|
173
|
-
:status => thread[:status],
|
174
|
-
:context => thread[:context],
|
175
|
-
}
|
176
|
-
end
|
177
|
-
output
|
201
|
+
# Collect info's
|
202
|
+
@@workers.list.map { |thread| thread.job }
|
178
203
|
end
|
179
204
|
|
205
|
+
def delete_job id
|
206
|
+
info "> delete_job(#{id})"
|
180
207
|
|
181
|
-
|
182
|
-
|
208
|
+
# Find jobs with this id
|
209
|
+
jobs = jobs_with_id id
|
183
210
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
211
|
+
# Kill them
|
212
|
+
jobs.each{ |thread| Thread.kill(thread) }
|
213
|
+
|
214
|
+
# Return the first one
|
215
|
+
return nil if jobs.empty?
|
216
|
+
jobs.first.job
|
217
|
+
end
|
218
|
+
|
219
|
+
def find_job id
|
220
|
+
info "> find_job(#{id})"
|
221
|
+
|
222
|
+
# Find jobs with this id
|
223
|
+
jobs = jobs_with_id id
|
224
|
+
|
225
|
+
# Return the first one
|
226
|
+
return nil if jobs.empty?
|
227
|
+
jobs.first.job
|
228
|
+
end
|
229
|
+
|
230
|
+
def jobs_with_id id
|
231
|
+
info "> find_jobs_by_id(#{id})"
|
232
|
+
@@workers.list.select{ |thread| thread[:id].to_s == id.to_s }
|
191
233
|
end
|
192
234
|
|
193
235
|
def new_job context = {}
|
@@ -196,15 +238,15 @@ class RestFtpDaemon < Sinatra::Base
|
|
196
238
|
# Generate name
|
197
239
|
@@last_worker_id +=1
|
198
240
|
host = @@hostname.split('.')[0]
|
199
|
-
|
200
|
-
|
241
|
+
worker_id = @@last_worker_id
|
242
|
+
worker_name = "#{host}-#{Process.pid.to_s}-#{worker_id}"
|
243
|
+
info "new_job: creating thread [#{worker_name}]"
|
201
244
|
|
202
245
|
# Parse parameters
|
203
246
|
job_source = context["source"]
|
204
247
|
job_target = context["target"]
|
205
248
|
return { code: ERR_REQ_SOURCE_MISSING, errmsg: :ERR_REQ_SOURCE_MISSING} if job_source.nil?
|
206
249
|
return { code: ERR_REQ_TARGET_MISSING, errmsg: :ERR_REQ_TARGET_MISSING} if job_target.nil?
|
207
|
-
#return { code: ERR_TRX_SOURCE_FILE_NOT_FOUND, errmsg: "ERR_TRX_SOURCE_FILE_NOT_FOUND [#{job_source}]"} unless File.exists? job_source
|
208
250
|
|
209
251
|
# Parse dest URI
|
210
252
|
target = URI(job_target)
|
@@ -212,47 +254,51 @@ class RestFtpDaemon < Sinatra::Base
|
|
212
254
|
return { code: ERR_REQ_TARGET_SCHEME, errmsg: :ERR_REQ_TARGET_SCHEME} unless target.scheme == "ftp"
|
213
255
|
|
214
256
|
# Create thread
|
215
|
-
job = Thread.new(
|
216
|
-
#
|
217
|
-
Thread.current[:name] = name
|
218
|
-
Thread.current[:created] = Time.now.to_f;
|
219
|
-
Thread.current[:status] = :thread_initializing
|
220
|
-
|
221
|
-
# Store job info
|
222
|
-
Thread.current[:context] = context
|
223
|
-
job_status ERR_BUSY, :thread_initializing
|
257
|
+
job = Thread.new(worker_id, worker_name, job) do
|
258
|
+
# Tnitialize thread
|
224
259
|
Thread.abort_on_exception = true
|
260
|
+
job_status :initializing
|
261
|
+
job_error ERR_OK
|
262
|
+
|
263
|
+
# Initialize job info
|
264
|
+
Thread.current[:job] = {}
|
265
|
+
Thread.current[:job].merge! context if context.is_a? Enumerable
|
266
|
+
Thread.current[:id] = worker_id
|
267
|
+
job_set :worker_name, worker_name
|
268
|
+
job_set :created, Time.now
|
225
269
|
|
226
270
|
# Do the job
|
227
271
|
info "new_job: thread running"
|
228
272
|
process_job
|
229
273
|
|
230
274
|
# Sleep a few seconds before dying
|
231
|
-
|
275
|
+
job_status :graceful_ending
|
232
276
|
sleep THREAD_SLEEP_BEFORE_DIE
|
277
|
+
job_status :ended
|
233
278
|
info "new_job: thread finished"
|
234
279
|
end
|
235
280
|
|
236
281
|
# Keep thread in thread group
|
237
|
-
info "new_job: attaching thread [#{
|
238
|
-
@@
|
282
|
+
info "new_job: attaching thread [#{worker_name}] to group"
|
283
|
+
@@workers.add job
|
239
284
|
|
240
|
-
return { code: 0, errmsg: 'success',
|
285
|
+
return { code: 0, errmsg: 'success', worker_id: worker_id, context: context }
|
241
286
|
end
|
242
287
|
|
243
288
|
def info msg=""
|
244
|
-
@logger.
|
289
|
+
@logger.info msg
|
245
290
|
end
|
246
291
|
|
247
|
-
def
|
248
|
-
|
249
|
-
|
250
|
-
|
292
|
+
def job_error error, errmsg = nil
|
293
|
+
job_set :error, error
|
294
|
+
job_set :errmsg, errmsg
|
295
|
+
end
|
296
|
+
def job_status status
|
297
|
+
job_set :status, status
|
251
298
|
end
|
252
299
|
|
253
|
-
def job_set attribute, value
|
254
|
-
|
255
|
-
Thread.current[:context][attribute] = value
|
300
|
+
def job_set attribute, value, thread = Thread.current
|
301
|
+
thread[:job][attribute] = value if thread[:job].is_a? Enumerable
|
256
302
|
end
|
257
303
|
|
258
304
|
|
data/rest-ftp-daemon.gemspec
CHANGED
@@ -5,12 +5,12 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "rest-ftp-daemon"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.7.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Bruno"]
|
12
|
-
s.date = "2014-
|
13
|
-
s.description = "
|
12
|
+
s.date = "2014-08-01"
|
13
|
+
s.description = "This is a pretty simple FTP client daemon, controlled through a RESTfull API"
|
14
14
|
s.email = "rest-ftp-daemon@bmconseil.com"
|
15
15
|
s.executables = ["rest-ftp-daemon"]
|
16
16
|
s.extra_rdoc_files = [
|
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
|
|
28
28
|
"lib/config.rb",
|
29
29
|
"lib/config.ru",
|
30
30
|
"lib/errors.rb",
|
31
|
+
"lib/extend_threads.rb",
|
31
32
|
"lib/rest-ftp-daemon.rb",
|
32
33
|
"rest-ftp-daemon.gemspec",
|
33
34
|
"test/helper.rb",
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest-ftp-daemon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-08-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sinatra
|
@@ -91,7 +91,8 @@ dependencies:
|
|
91
91
|
- - ~>
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: 2.0.1
|
94
|
-
description:
|
94
|
+
description: This is a pretty simple FTP client daemon, controlled through a RESTfull
|
95
|
+
API
|
95
96
|
email: rest-ftp-daemon@bmconseil.com
|
96
97
|
executables:
|
97
98
|
- rest-ftp-daemon
|
@@ -110,6 +111,7 @@ files:
|
|
110
111
|
- lib/config.rb
|
111
112
|
- lib/config.ru
|
112
113
|
- lib/errors.rb
|
114
|
+
- lib/extend_threads.rb
|
113
115
|
- lib/rest-ftp-daemon.rb
|
114
116
|
- rest-ftp-daemon.gemspec
|
115
117
|
- test/helper.rb
|
@@ -129,7 +131,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
129
131
|
version: '0'
|
130
132
|
segments:
|
131
133
|
- 0
|
132
|
-
hash:
|
134
|
+
hash: 3650939871823663291
|
133
135
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
136
|
none: false
|
135
137
|
requirements:
|