iron_worker 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown ADDED
@@ -0,0 +1,72 @@
1
+ Getting Started
2
+ ===============
3
+
4
+ [Sign up for a IronWorker account][1], it's free to try!
5
+
6
+ [1]: http://www.iron.io/
7
+
8
+ Install IronWorker Gem
9
+ ------------------------
10
+
11
+ gem install iron_worker
12
+
13
+ Configure IronWorker
14
+ ----------------------
15
+
16
+ You really just need your token, which you can get [here][2]
17
+ [2]: http://hud.iron.io/tokens
18
+
19
+ IronWorker.configure do |config|
20
+ config.token = TOKEN
21
+ config.project_id = MY_PROJECT_ID
22
+ end
23
+
24
+ Write a Worker
25
+ --------------
26
+
27
+ Here's an example worker that sends an email:
28
+
29
+ require 'iron_worker'
30
+
31
+ class HelloWorker < IronWorker::Base
32
+
33
+ attr_accessor :name
34
+
35
+ # This is the method that will be run
36
+ def run
37
+ puts "Hello #{name}!"
38
+ end
39
+ end
40
+
41
+ Test It Locally
42
+ ---------------
43
+
44
+ Let's say someone does something in your app and you want to send an email about it.
45
+
46
+ worker = HelloWorker.new
47
+ worker.name = "Travis"
48
+ worker.run_local
49
+
50
+ Once you've got it working locally, the next step is to run it on the IronWorker cloud.
51
+
52
+ Queue up your Worker on the IronWorker Cloud
53
+ ----------------------------------------------
54
+
55
+ Let's say someone does something in your app and you want to send an email about it.
56
+
57
+ worker = HelloWorker.new
58
+ worker.name = "Travis"
59
+ worker.queue
60
+
61
+ This will send it off to the IronWorker cloud.
62
+
63
+ Full Documentation
64
+ -----------------
65
+
66
+ Now that you've got your first worker running, be sure to [check out the full documentation](http://docs.iron.io).
67
+ IronWorker can do so much more!
68
+
69
+ Discussion Group
70
+ ----------------------
71
+
72
+ Join the discussion group at: https://groups.google.com/forum/#!forum/ironworker-users
data/VERSION.yml ADDED
@@ -0,0 +1,5 @@
1
+ ---
2
+ :major: 2
3
+ :minor: 1
4
+ :patch: 0
5
+ :build:
@@ -0,0 +1,13 @@
1
+ class IronWorkerGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path("../templates", __FILE__)
3
+
4
+ desc "Creates a new skeleton worker - NAME is camelized"
5
+ def create_worker_file
6
+ # file_name needs to be classified
7
+ @camel = file_name.camelize
8
+ if not File.directory? "#{Rails.root}/app/workers"
9
+ Dir.mkdir "#{Rails.root}/app/workers"
10
+ end
11
+ template "template_worker.erb", "app/workers/#{file_name}.rb"
12
+ end
13
+ end
@@ -0,0 +1,49 @@
1
+ require_relative 'iron_worker/utils'
2
+ require_relative 'iron_worker/service'
3
+ require_relative 'iron_worker/base'
4
+ require_relative 'iron_worker/config'
5
+ require_relative 'iron_worker/used_in_worker'
6
+
7
+
8
+ module IronWorker
9
+ @@logger = Logger.new(STDOUT)
10
+ @@logger.level = Logger::INFO
11
+
12
+
13
+ class << self
14
+ attr_accessor :config,
15
+ :service
16
+
17
+ def configure()
18
+ yield(config)
19
+ if config && config.token
20
+ IronWorker.service ||= Service.new(config.token, :config=>config)
21
+ else
22
+ @@logger.warn "No token specified in configure, be sure to set it!"
23
+ end
24
+ end
25
+
26
+ def config
27
+ @config ||= Config.new
28
+ end
29
+
30
+ def logger
31
+ @@logger
32
+ end
33
+
34
+ def api_version
35
+ 2
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ if defined?(Rails)
42
+ # puts 'Rails=' + Rails.inspect
43
+ # puts 'vers=' + Rails::VERSION::MAJOR.inspect
44
+ if Rails::VERSION::MAJOR == 2
45
+ require_relative 'iron_worker/rails2_init.rb'
46
+ else
47
+ require_relative 'iron_worker/railtie'
48
+ end
49
+ end
@@ -0,0 +1,242 @@
1
+ require 'rest_client'
2
+ require_relative 'uber_client'
3
+
4
+ module IronWorker
5
+
6
+ class RequestError < StandardError
7
+ def initialize(msg, options={})
8
+ super(msg)
9
+ @options = options
10
+ end
11
+
12
+ def status
13
+ @options[:status]
14
+ end
15
+ end
16
+
17
+ module Api
18
+
19
+ module Signatures
20
+
21
+
22
+ def self.generate_timestamp(gmtime)
23
+ return gmtime.strftime("%Y-%m-%dT%H:%M:%SZ")
24
+ end
25
+
26
+
27
+ def self.generate_signature(operation, timestamp, secret_key)
28
+ my_sha_hmac = Digest::HMAC.digest(operation + timestamp, secret_key, Digest::SHA1)
29
+ my_b64_hmac_digest = Base64.encode64(my_sha_hmac).strip
30
+ return my_b64_hmac_digest
31
+ end
32
+
33
+
34
+ def self.hash_to_s(hash)
35
+ str = ""
36
+ hash.sort.each { |a| str+= "#{a[0]}#{a[1]}" }
37
+ #removing all characters that could differ after parsing with rails
38
+ return str.delete "\"\/:{}[]\' T"
39
+ end
40
+ end
41
+
42
+ # Subclass must define:
43
+ # host: endpoint url for service
44
+ class Client
45
+
46
+ attr_accessor :scheme, :host, :port, :token, :version, :config
47
+
48
+ def initialize(host, token, options={})
49
+ @config = options[:config]
50
+ @scheme = options[:scheme] || @config.scheme || "https"
51
+ @host = options[:host] || @config.host || host
52
+ @port = options[:port] || @config.port || 443
53
+ @token = options[:token] || @config.token || token
54
+ @version = options[:version]
55
+ #@logger = options[:logger]
56
+
57
+ @base_url = "#{@scheme}://#{@host}:#{@port}/#{@version}"
58
+
59
+ @uber_client = Uber::Client.new
60
+
61
+ end
62
+
63
+
64
+ def base_url
65
+ @base_url
66
+ end
67
+
68
+ def url(command_path)
69
+ # @logger.debug "url: " + url.to_s
70
+ "/#{command_path}"
71
+ end
72
+
73
+ def url_full(command_path)
74
+ url = "#{base_url}/#{command_path}"
75
+ # @logger.debug "url: " + url.to_s
76
+ url
77
+ end
78
+
79
+
80
+ def common_req_hash
81
+ {
82
+ :headers=>{"Content-Type" => 'application/json',
83
+ "Authorization"=>"OAuth #{@token}",
84
+ "User-Agent"=>"IronWorker Ruby Client"}
85
+ }
86
+ end
87
+
88
+ def process_ex(ex)
89
+ logger.error "EX #{ex.class.name}: #{ex.message}"
90
+ body = ex.http_body
91
+ logger.debug 'EX http_code: ' + ex.http_code.to_s
92
+ logger.debug 'EX BODY=' + body.to_s
93
+ decoded_ex = JSON.parse(body)
94
+ exception = Exception.new(ex.message + ": " + decoded_ex["msg"])
95
+ exception.set_backtrace(decoded_ex["backtrace"].split(",")) if decoded_ex["backtrace"]
96
+ raise exception
97
+ end
98
+
99
+
100
+ def check_response(response, options={})
101
+ # response.code # http status code
102
+ #response.time # time in seconds the request took
103
+ #response.headers # the http headers
104
+ #response.headers_hash # http headers put into a hash
105
+ #response.body # the response body
106
+ status = response.code
107
+ body = response.body
108
+ # todo: check content-type == application/json before parsing
109
+ logger.debug "response code=" + status.to_s
110
+ logger.debug "response body=" + body.inspect
111
+ res = nil
112
+ unless options[:parse] == false
113
+ res = JSON.parse(body)
114
+ end
115
+ if status < 400
116
+
117
+ else
118
+ raise IronWorker::RequestError.new((res ? "#{status}: #{res["msg"]}" : "#{status} Error! parse=false so no msg"), :status=>status)
119
+ end
120
+ res || body
121
+ end
122
+
123
+ def get(method, params={}, options={})
124
+ full_url = url_full(method)
125
+ #all_params = add_params(method, params)
126
+ #url_plus_params = append_params(full_url, all_params)
127
+ logger.debug 'get url=' + full_url
128
+ req_hash = common_req_hash
129
+ req_hash[:params] = params
130
+ response = @uber_client.get(full_url, req_hash) # could let typhoeus add params, using :params=>x
131
+ #response = @http_sess.request(:get, url_plus_params,
132
+ # {},
133
+ # {})
134
+ check_response(response, options)
135
+ body = response.body
136
+ parse_response(body, options)
137
+
138
+ end
139
+
140
+ def post_file(method, file, params={}, options={})
141
+ begin
142
+ data = add_params(method, params).to_json
143
+ url = url_full(method)
144
+ logger.debug "post_file url = " + url
145
+ logger.debug "data = " + data
146
+ logger.debug "params = " + params.inspect
147
+ logger.debug "options = " + options.inspect
148
+ # todo: replace with uber_client
149
+ parse_response(RestClient.post(append_params(url, add_params(method, params)), {:data => data, :file => file}, :content_type => 'application/json'), options)
150
+ rescue RestClient::Exception => ex
151
+ process_ex(ex)
152
+ end
153
+ end
154
+
155
+ def post(method, params={}, options={})
156
+ logger.debug "params = " + params.inspect
157
+ logger.debug "options = " + options.inspect
158
+ logger.debug "params.payload = " + params[:payload].inspect
159
+ logger.debug "token = "+ token.inspect
160
+ begin
161
+ url = url_full(method)
162
+ logger.debug 'post url=' + url
163
+ json = add_params(method, params).to_json
164
+ logger.debug 'body=' + json
165
+ req_hash = common_req_hash
166
+ req_hash[:body] = json
167
+ response = @uber_client.post(url, req_hash)
168
+ #response = @http_sess.post(url, json, {"Content-Type" => 'application/json'})
169
+ check_response(response)
170
+ logger.debug 'response: ' + response.inspect
171
+ body = response.body
172
+ parse_response(body, options)
173
+ rescue IronWorker::RequestError => ex
174
+ # let it throw, came from check_response
175
+ raise ex
176
+ rescue RestClient::Exception => ex
177
+ logger.warn("Exception in post! #{ex.message}")
178
+ logger.warn(ex.backtrace.join("\n"))
179
+ process_ex(ex)
180
+ end
181
+ end
182
+
183
+
184
+ def put(method, body, options={})
185
+ begin
186
+ # todo: replace with uber_client
187
+ parse_response RestClient.put(url_full(method), add_params(method, body).to_json, headers), options
188
+ rescue RestClient::Exception => ex
189
+ process_ex(ex)
190
+ end
191
+ end
192
+
193
+ def delete(method, params={}, options={})
194
+ begin
195
+ # todo: replace with uber_client
196
+ parse_response RestClient.delete(append_params(url_full(method), add_params(method, params))), options
197
+ rescue RestClient::Exception => ex
198
+ process_ex(ex)
199
+ end
200
+ end
201
+
202
+ def add_params(command_path, hash)
203
+ extra_params = {'oauth' => token}
204
+ hash.merge!(extra_params)
205
+ end
206
+
207
+ def append_params(host, params)
208
+ host += "?"
209
+ i = 0
210
+ params.each_pair do |k, v|
211
+ #puts "k=#{k} v=#{v}"
212
+ host += "&" if i > 0
213
+ host += k + "=" + (v.is_a?(String) ? CGI.escape(v) : v.to_s)
214
+ i +=1
215
+ end
216
+ return host
217
+ end
218
+
219
+ def headers
220
+ user_agent = "IronWorker Ruby Client"
221
+ headers = {'User-Agent' => user_agent}
222
+ end
223
+
224
+ def parse_response(response, options={})
225
+ #puts 'PARSE RESPONSE: ' + response.to_s
226
+ unless options[:parse] == false
227
+ begin
228
+ return JSON.parse(response.to_s)
229
+ rescue => ex
230
+ puts 'parse_response: response that caused error = ' + response.to_s
231
+ raise ex
232
+ end
233
+ else
234
+ response
235
+ end
236
+ end
237
+
238
+ end
239
+
240
+ end
241
+
242
+ end
@@ -0,0 +1,432 @@
1
+ require 'digest/md5'
2
+ require 'base64'
3
+
4
+ module IronWorker
5
+
6
+ class Base
7
+
8
+ attr_accessor :task_set_id, :task_id, :schedule_id
9
+ attr_reader :response
10
+
11
+ class << self
12
+ attr_accessor :subclass, :caller_file
13
+ @merged = {}
14
+ @merged_workers = {}
15
+ @merged_gems = {}
16
+ @merged_mailers = {}
17
+ @merged_folders = {}
18
+ @unmerged = {}
19
+ @unmerged_gems = {}
20
+
21
+ def reset!
22
+ @merged = {}
23
+ @merged_workers = {}
24
+ @merged_gems = {}
25
+ @merged_mailers = {}
26
+ @merged_folders = {}
27
+ @unmerged = {}
28
+ @unmerged_gems = {}
29
+ end
30
+
31
+ def inherited(subclass)
32
+ subclass.reset!
33
+
34
+ caller_file = caller[0][0...(caller[0].index(":in"))]
35
+ caller_file = caller_file[0...(caller_file.rindex(":"))]
36
+ subclass.instance_variable_set(:@caller_file, caller_file)
37
+
38
+ super
39
+ end
40
+
41
+ # merges the specified gem.
42
+ def merge_gem(gem_name, options={})
43
+ gem_info = IronWorker::MergeHelper.create_gem_info(gem_name, options)
44
+ @merged_gems[gem_name.to_s] = gem_info
45
+ reqs = gem_info[:require].is_a?(Array) ? gem_info[:require] : [gem_info[:require]]
46
+ reqs.each do |r|
47
+ r2 = "#{gem_info[:path]}/lib/#{r}"
48
+ begin
49
+ IronWorker.logger.debug 'requiring ' + r2
50
+ require r2
51
+ rescue LoadError=>ex
52
+ IronWorker.logger.error "Error requiring gem #{r}: #{ex.message}"
53
+ raise "Gem #{gem_name} was found, but we could not load the file '#{r2}'. You may need to use :require=>x.........."
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ def unmerge_gem(gem_name)
60
+ #gem_info = {:name=>gem_name}
61
+ #@unmerged_gems[gem_name.to_s] = gem_info
62
+ gs = gem_name.to_s
63
+ gem_info = {:name=>gs}
64
+ @unmerged_gems[gs] = gem_info
65
+ @merged_gems.delete(gs)
66
+ end
67
+
68
+ #merge action_mailer mailers
69
+ def merge_mailer(mailer, params={})
70
+ f2 = IronWorker::MergeHelper.check_for_file(mailer, @caller_file)
71
+ basename = File.basename(mailer, f2[:extname])
72
+ path_to_templates = params[:path_to_templates] || File.join(Rails.root, "app/views/#{basename}")
73
+ @merged_mailers[basename] = {:name=>basename, :path_to_templates=>path_to_templates, :filename => f2[:path]}.merge!(params)
74
+ end
75
+
76
+ def merge_folder(path)
77
+ files = []
78
+ #puts "caller_file=" + caller_file
79
+ if path[0, 1] == '/'
80
+ abs_dir = path
81
+ else # relative
82
+ abs_dir = File.join(File.dirname(caller_file), path)
83
+ end
84
+ #puts 'abs_dir=' + abs_dir
85
+ raise "Folder not found for merge_folder #{path}!" unless File.directory?(abs_dir)
86
+ rbfiles = File.join(abs_dir, "*.rb")
87
+ Dir[rbfiles].each do |f|
88
+ #f2 = check_for_file(f)
89
+ #puts "f2=#{f2}"
90
+ merge(f)
91
+ #files << f
92
+ #@merged[f]
93
+ end
94
+ #@merged_folders[path] = files unless files.empty?
95
+ #IronWorker.logger.info "Merged folders! #{@merged_folders.inspect}"
96
+ end
97
+
98
+ # merges the specified file.
99
+ #
100
+ # Example: merge 'models/my_model'
101
+ def merge(f)
102
+ f2 = IronWorker::MergeHelper.check_for_file(f, @caller_file)
103
+ fbase = f2[:basename]
104
+ ret = f2
105
+ @merged[fbase] = ret
106
+ ret
107
+ end
108
+
109
+ # Opposite of merge, this will omit the files you specify from being merged in. Useful in Rails apps
110
+ # where a lot of things are auto-merged by default like your models.
111
+ def unmerge(f)
112
+ f2 = IronWorker::MergeHelper.check_for_file(f, @caller_file)
113
+ fbase = f2[:basename]
114
+ @unmerged[fbase] = f2
115
+ @merged.delete(fbase)
116
+ end
117
+
118
+
119
+ # Use this to merge in other workers. These are treated differently the normal merged files because
120
+ # they will be uploaded separately and treated as distinctly separate workers.
121
+ #
122
+ # file: This is the path to the file, just like merge.
123
+ # class_name: eg: 'MyWorker'.
124
+ def merge_worker(file, class_name)
125
+ # puts 'merge_worker in ' + self.name
126
+ ret = merge(file)
127
+ ret[:class_name] = class_name
128
+ #[File.expand_path(file), class_name]
129
+ @merged_workers[file] = ret
130
+ ret
131
+ end
132
+ end
133
+
134
+
135
+ def log(str)
136
+ puts str.to_s
137
+ end
138
+
139
+ def user_dir
140
+ "./"
141
+ end
142
+
143
+ def set_progress(hash)
144
+ puts 'set_progress: ' + hash.inspect
145
+ end
146
+
147
+ def who_am_i?
148
+ return self.class.name
149
+ end
150
+
151
+ def uploaded?
152
+ self.class.instance_variable_defined?(:@uploaded) && self.class.instance_variable_get(:@uploaded)
153
+ end
154
+
155
+ # Call this if you want to run locally and get some extra features from this gem like global attributes.
156
+ def run_local
157
+ # puts 'run_local'
158
+ set_auto_attributes
159
+ init_database
160
+ init_mailer
161
+ begin
162
+ run
163
+ rescue => ex
164
+ if self.respond_to?(:rescue_all)
165
+ rescue_all(ex)
166
+ else
167
+ raise ex
168
+ end
169
+ end
170
+ end
171
+
172
+ def init_mailer
173
+ if IronWorker.config.mailer
174
+ require 'action_mailer'
175
+ ActionMailer::Base.raise_delivery_errors = true
176
+ ActionMailer::Base.smtp_settings = (IronWorker.config.mailer)
177
+ end
178
+ end
179
+
180
+ def init_database
181
+ if IronWorker.config.database
182
+ require 'active_record'
183
+ if !ActiveRecord::Base.connected?
184
+ ActiveRecord::Base.establish_connection(IronWorker.config.database)
185
+ end
186
+ end
187
+ end
188
+
189
+ def set_auto_attributes
190
+ set_global_attributes
191
+ end
192
+
193
+ def set_global_attributes
194
+ return unless IronWorker.config
195
+ ga = IronWorker.config.global_attributes
196
+ if ga && ga.size > 0
197
+ ga.each_pair do |k, v|
198
+ # puts "k=#{k} v=#{v}"
199
+ if self.respond_to?(k)
200
+ self.send("#{k}=", v)
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ def enqueue(options={})
207
+ queue(options)
208
+ end
209
+
210
+ # Call this to queue up your job to IronWorker cloud.
211
+ # options:
212
+ # :priority => 0, 1 or 2. Default is 0.
213
+ # :recursive => true/false. Default is false. If you queue up a worker that is the same class as the currently
214
+ # running worker, it will be rejected unless you set this explicitly so we know you meant to do it.
215
+ def queue(options={})
216
+ # puts 'in queue'
217
+ set_auto_attributes
218
+ upload_if_needed(options)
219
+
220
+ response = IronWorker.service.queue(self.class.name, sw_get_data, options)
221
+ IronWorker.service.logger.debug 'queue response=' + response.inspect
222
+ @response = response
223
+ @task_id = response["tasks"][0]["id"]
224
+ response
225
+ end
226
+
227
+ # Receive the status of your worker.
228
+ def status
229
+ check_service
230
+ if task_id
231
+ task_status
232
+ elsif schedule_id
233
+ schedule_status
234
+ else
235
+ raise "Queue or schedule before check status."
236
+ end
237
+ end
238
+
239
+ def task_status
240
+ IronWorker.service.status(task_id)
241
+ end
242
+
243
+ def is_local?
244
+ !is_remote?
245
+ end
246
+
247
+ def is_remote?
248
+ false
249
+ end
250
+
251
+ # will return after job has completed or errored out.
252
+ # Returns status.
253
+ # todo: add a :timeout option
254
+ def wait_until_complete
255
+ check_service
256
+ IronWorker.service.wait_until_complete(self.task_id)
257
+ end
258
+
259
+ def upload
260
+ upload_if_needed
261
+ end
262
+
263
+ #
264
+ # schedule: hash of scheduling options that can include:
265
+ # Required:
266
+ # - start_at: Time of first run - DateTime or Time object.
267
+ # Optional:
268
+ # - run_every: Time in seconds between runs. If ommitted, task will only run once.
269
+ # - delay_type: Fixed Rate or Fixed Delay. Default is fixed_delay.
270
+ # - end_at: Scheduled task will stop running after this date (optional, if ommitted, runs forever or until cancelled)
271
+ # - run_times: Task will run exactly :run_times. For instance if :run_times is 5, then the task will run 5 times.
272
+ # - name: Provide a name for the schedule, defaults to class name. Use this if you want more than one schedule per worker class.
273
+ #
274
+ def schedule(schedule)
275
+ set_global_attributes
276
+ upload_if_needed(schedule)
277
+
278
+ response = IronWorker.service.schedule(self.class.name, sw_get_data, schedule)
279
+ IronWorker.service.logger.debug 'schedule response=' + response.inspect
280
+ @schedule_id = response["schedules"][0]["id"]
281
+ response
282
+ end
283
+
284
+ def schedule_status
285
+ IronWorker.service.schedule_status(schedule_id)
286
+ end
287
+
288
+ # Retrieves the log for this worker from the IronWorker service.
289
+ def get_log(options={})
290
+ IronWorker.service.get_log(task_id, options)
291
+ end
292
+
293
+ # Callbacks for developer
294
+ def before_upload
295
+
296
+ end
297
+
298
+ def after_upload
299
+
300
+ end
301
+
302
+ def before_run
303
+
304
+ end
305
+
306
+ def after_run
307
+
308
+ end
309
+
310
+ private
311
+
312
+ def gems_to_merge(merged_gems)
313
+ list_of_gems = {}
314
+ if merged_gems && merged_gems.size > 0
315
+ installed_gems = IronWorker.config.get_server_gems
316
+ merged_gems.each_pair do |k, gem|
317
+ gem.merge!({:merge=>(!installed_gems.find { |g| g["name"]==gem[:name] && g["version"]==gem[:version] })})
318
+ list_of_gems[gem[:name]] = gem # don't' need this if (list_of_gems.select { |k,g| g[:name]==gem[:name] }).empty?
319
+ end
320
+ IronWorker.logger.debug "#{list_of_gems.inspect}"
321
+ end
322
+ list_of_gems
323
+ end
324
+
325
+ def check_service
326
+ raise "IronWorker configuration not set." unless IronWorker.service
327
+ end
328
+
329
+ def self.extract_superclasses_merges(worker, merged)
330
+ subclass = worker.class
331
+ rfile = subclass.instance_variable_get(:@caller_file) # Base.caller_file # File.expand_path(Base.subclass)
332
+ # puts 'subclass file=' + rfile.inspect
333
+ # puts 'subclass.name=' + subclass.name
334
+ superclass = subclass
335
+ # Also get merged from subclasses up to IronWorker::Base
336
+ while (superclass = superclass.superclass)
337
+ #puts 'superclass=' + superclass.name
338
+ break if superclass.name == IronWorker::Base.name
339
+ super_merged = superclass.instance_variable_get(:@merged)
340
+ #puts 'merging caller file: ' + superclass.instance_variable_get(:@caller_file).inspect
341
+ caller_to_add = superclass.instance_variable_get(:@caller_file)
342
+ fb = File.basename(caller_to_add)
343
+ r = {:name=>fb, :path=>caller_to_add}
344
+ super_merged[fb] = r
345
+ merged.merge!(super_merged)
346
+ #puts 'merged with superclass=' + merged.inspect
347
+ end
348
+ return merged, rfile, subclass
349
+ end
350
+
351
+ def self.extract_merged_workers(worker)
352
+ merged_workers = worker.class.instance_variable_get(:@merged_workers)
353
+ IronWorker.logger.debug "Looking for merged_workers in #{worker.class.name}: #{merged_workers.inspect}"
354
+ ret = {}
355
+ if merged_workers && merged_workers.size > 0
356
+ merged_workers.each_pair do |k, mw|
357
+ IronWorker.logger.debug "merged worker found in #{worker.class.name}: #{mw.inspect}"
358
+ ret[mw[:name]] = mw
359
+ end
360
+ end
361
+ ret
362
+ end
363
+
364
+ def upload_if_needed(options={})
365
+ check_service
366
+ IronWorker.service.check_config
367
+
368
+ before_upload
369
+
370
+ merged = self.class.instance_variable_get(:@merged)
371
+
372
+ # do merged_workers first because we need to get their subclasses and what not too
373
+ merged_workers = self.class.instance_variable_get(:@merged_workers)
374
+ if merged_workers && merged_workers.size > 0
375
+ IronWorker.logger.debug 'now uploading merged workers ' + merged_workers.inspect
376
+ merged_workers.each_pair do |mw, v|
377
+ IronWorker.logger.debug 'Instantiating and uploading ' + v.inspect
378
+ mw_instantiated = Kernel.const_get(v[:class_name]).new
379
+ mw_instantiated.upload
380
+
381
+ merged, rfile, subclass = IronWorker::Base.extract_superclasses_merges(mw_instantiated, merged)
382
+ merged.merge!(IronWorker::Base.extract_merged_workers(mw_instantiated))
383
+
384
+ end
385
+ end
386
+
387
+ if !uploaded?
388
+ unmerged = self.class.instance_variable_get(:@unmerged)
389
+ merged_gems = self.class.instance_variable_get(:@merged_gems)
390
+ merged_mailers = self.class.instance_variable_get(:@merged_mailers)
391
+ merged_folders = self.class.instance_variable_get(:@merged_folders)
392
+ merged, rfile, subclass = IronWorker::Base.extract_superclasses_merges(self, merged)
393
+ merged_mailers = merged_mailers.merge(IronWorker.config.mailers) if IronWorker.config.mailers
394
+ unless merged_gems.size == 0
395
+ merged_gems = gems_to_merge(merged_gems)
396
+ end
397
+
398
+ options_for_upload = {:merge=>merged, :unmerge=>unmerged, :merged_gems=>merged_gems, :merged_mailers=>merged_mailers, :merged_folders=>merged_folders}
399
+ options_for_upload.merge!(options)
400
+ IronWorker.service.upload(rfile, subclass.name, options_for_upload)
401
+ self.class.instance_variable_set(:@uploaded, true)
402
+ else
403
+ IronWorker.logger.debug 'Already uploaded for ' + self.class.name
404
+ end
405
+
406
+ after_upload
407
+ end
408
+
409
+ def sw_get_data
410
+ data = {}
411
+
412
+ payload = {}
413
+ # todo: should put these down a layer, eg: payload[:attributes]
414
+ self.instance_variables.each do |iv|
415
+ payload[iv] = instance_variable_get(iv)
416
+ end
417
+ data['class_name'] = self.class.name
418
+ data[:attr_encoded] = Base64.encode64(payload.to_json)
419
+ data[:file_name] = File.basename(self.class.instance_variable_get(:@caller_file))
420
+ if defined?(Rails)
421
+ data[:rails] = {}
422
+ data[:rails]['env'] = Rails.env
423
+ data[:rails]['version'] = Rails.version
424
+ end
425
+ config_data = IronWorker.config.get_atts_to_send
426
+ data[:sw_config] = config_data
427
+ return data
428
+ end
429
+
430
+
431
+ end
432
+ end