jobler 0.0.8 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a515f6a2b9fd3b0fbf184f612d2665c2b8934bda
4
- data.tar.gz: f0384836dcf388c9d5f5bfdc60d4e524a9539792
2
+ SHA256:
3
+ metadata.gz: 549a9f294ff023f7c67091e91120706db134cd9639d9f8b2d60d9860c5562788
4
+ data.tar.gz: '097746283982a71eeffdd1a006e2e8a6d178c5b847e6854ddae0696075da4490'
5
5
  SHA512:
6
- metadata.gz: 6a7e9bd7fba0d0a0bd14d82d3075cf120a69dfe51f6986855ee4b2bfee08a6f0ba5f6add226ea0f3b47ab6d1f9cdcbc33790f907709c267f214ccb36ff8ce136
7
- data.tar.gz: 501a4c6a6a5455d4e54afac0a14e891dc098bb2747882ee5c462dd9001bd4218665b1533874b4db7605603a1b34fb725348f992bbc3a89f20fb686d786f3f348
6
+ metadata.gz: 3204ebaef1538454ea89a42066fc498d554317f14a9dd72e03356c1c1cb58ef5aac58909246c750b17c785b4610b33cef3a7b19ab93cc3877297ae0b659025e6
7
+ data.tar.gz: e7ffb22951a39418e2ffd48c7f58b69b7465dbf41419583c5b49ed8ae71fbcbd15ca5dd7f58491df70ec7769c2e1dadb2416bb7ceac974910d50ef8af7617a23
data/README.md CHANGED
@@ -34,7 +34,11 @@ class ApplicationJobler < Jobler::BaseJobler
34
34
  end
35
35
  ```
36
36
 
37
- Jobler is going to queue its jobs through the ActiveJob queue called `:jobler`, so make sure a worker is listening to that queue.
37
+ Jobler is going to queue its jobs through the ActiveJob queue called `:jobler`, so make sure a worker is listening to that queue. This is done like this in Sidekiq:
38
+
39
+ ```bash
40
+ bundle exec sidekiq --queue default --queue jobler --queue mailers
41
+ ```
38
42
 
39
43
 
40
44
  ## Usage
@@ -111,7 +115,7 @@ You should then create a controller something like this:
111
115
  ```ruby
112
116
  class JoblerJobsController < ApplicationController
113
117
  def show
114
- @job = Jobler::Job.find_by!(slug: param[:id])
118
+ @job = Jobler::Job.find_by!(slug: params[:id])
115
119
  @result = @job.results.find_by!(name: "render")
116
120
  end
117
121
  end
@@ -119,7 +123,7 @@ end
119
123
 
120
124
  And a view in "app/views/jobler_jobs/show.html.erb":
121
125
  ```erb
122
- <%= @result.result.force_encoding("utf-8").html_safe
126
+ <%= @result.result.force_encoding("utf-8").html_safe %>
123
127
  ```
124
128
 
125
129
  You should also add a route like this:
@@ -129,6 +133,33 @@ Rails.application.routes.draw do
129
133
  end
130
134
  ```
131
135
 
136
+ # Progress bar
137
+
138
+ In order to utilize the progress bar and return some feedback on the progress to the user, you can implement a couple of calls to do that:
139
+
140
+ ```ruby
141
+ class MyJobler < ApplicationJobler
142
+ def execute!
143
+ progress_total collection.size
144
+
145
+ collection.find_each do |model|
146
+ increment_progress!
147
+ end
148
+ end
149
+ end
150
+ ```
151
+
152
+ You can also call it from a view, if you a doing a render like this:
153
+ ```erb
154
+ <% jobler.increment_progress! %>
155
+ ```
156
+
157
+ You can also specify a custom value if it isn't 1:
158
+ ```erb
159
+ <% jobler.increment_progress!(value: 5.0) %>
160
+ ```
161
+
132
162
  ## License
133
163
 
134
164
  This project rocks and uses MIT-LICENSE.
165
+
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
14
14
  rdoc.rdoc_files.include("lib/**/*.rb")
15
15
  end
16
16
 
17
- APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
17
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
18
18
  load "rails/tasks/engine.rake"
19
19
 
20
20
  load "rails/tasks/statistics.rake"
@@ -1,3 +1,2 @@
1
- //= require jquery
2
1
  //= require jobler/timeago
3
2
  //= require jobler/jobs
@@ -4,7 +4,11 @@ class Jobler::DownloadsController < Jobler::ApplicationController
4
4
  @result = @job.jobler.result
5
5
 
6
6
  if @result.is_a?(Jobler::FileDownload)
7
- send_file @result.temp_file.path, disposition: "attachment", filename: @result.file_name
7
+ if @result.url.present?
8
+ redirect_to @result.url
9
+ else
10
+ send_file @result.temp_file.path, disposition: "attachment", filename: @result.file_name
11
+ end
8
12
  else
9
13
  flash[:error] = "The result wasn't a file download"
10
14
  redirect_to :back
@@ -1,7 +1,6 @@
1
1
  class Jobler::JobsController < Jobler::ApplicationController
2
2
  def show
3
3
  @job = Jobler::Job.find_by!(slug: params[:id])
4
- @result = @job.jobler.result if @job.completed?
5
4
 
6
5
  respond_to do |format|
7
6
  format.json do
@@ -14,8 +13,17 @@ class Jobler::JobsController < Jobler::ApplicationController
14
13
  }
15
14
  end
16
15
 
17
- if @result.is_a?(Jobler::RedirectTo)
18
- format.html { redirect_to @result.url }
16
+ if @job.completed?
17
+ @job.jobler.controller = self
18
+ @job.jobler.format = format
19
+
20
+ @result = @job.jobler.result
21
+
22
+ if @result.is_a?(Jobler::RedirectTo)
23
+ format.html { redirect_to @result.url }
24
+ else
25
+ format.html
26
+ end
19
27
  else
20
28
  format.html
21
29
  end
@@ -0,0 +1,65 @@
1
+ class Jobler::Models::DestroyerJobler < Jobler::BaseJobler
2
+ def execute!
3
+ calculate_numbers
4
+
5
+ model_class.transaction do
6
+ destroy_relationships
7
+ model.destroy!
8
+ end
9
+ end
10
+
11
+ def result
12
+ format.html { controller.redirect_to args.fetch(:redirect_to) }
13
+ end
14
+
15
+ private
16
+
17
+ def calculate_numbers
18
+ Rails.logger.debug "Calculate numbers"
19
+
20
+ @total = 0
21
+ relationships.each do |relationship|
22
+ Rails.logger.debug "Calculate size for #{relationship}"
23
+ @total += model.__send__(relationship).size
24
+ end
25
+
26
+ progress_total @total
27
+
28
+ Rails.logger.debug "Done calculating numbers: #{@total}"
29
+ end
30
+
31
+ def destroy_relationships
32
+ Rails.logger.debug "Destroying relationships"
33
+ relationships.each do |relationship|
34
+ Rails.logger.debug "Destroying #{relationship}"
35
+ model.__send__(relationship).find_each do |sub_model|
36
+ Rails.logger.debug "Destroying #{sub_model.id}"
37
+ sub_model.destroy!
38
+ increment_progress!
39
+ end
40
+ end
41
+ end
42
+
43
+ def model_class
44
+ @model_class ||= args.fetch(:model).constantize
45
+ end
46
+
47
+ def model
48
+ @model ||= model_class.find(args.fetch(:model_id))
49
+ end
50
+
51
+ def relationships
52
+ @relationships ||= begin
53
+ result = []
54
+
55
+ model_class.reflections.each_value do |reflection|
56
+ next unless reflection.is_a?(ActiveRecord::Reflection::HasManyReflection)
57
+ next unless reflection.options[:dependent] == :destroy
58
+
59
+ result << reflection.name
60
+ end
61
+
62
+ result
63
+ end
64
+ end
65
+ end
@@ -1,15 +1,12 @@
1
- class Jobler::Job < ActiveRecord::Base
2
- has_many :results, class_name: "Jobler::Result", dependent: :destroy
1
+ class Jobler::Job < ActiveRecord::Base # rubocop:disable Rails/ApplicationRecord
2
+ has_many :results, class_name: "Jobler::Result", dependent: :destroy, inverse_of: :job
3
3
 
4
4
  validates :jobler_type, :slug, :state, presence: true
5
5
 
6
6
  before_validation :set_slug
7
7
 
8
8
  def jobler
9
- user_jobler = jobler_type.constantize.new
10
- user_jobler.instance_variable_set(:@args, YAML.load(parameters)) # rubocop:disable Security/YAMLLoad
11
- user_jobler.instance_variable_set(:@job, self)
12
- user_jobler
9
+ @jobler ||= jobler_type.constantize.new(args: YAML.load(parameters), job: self) # rubocop:disable Security/YAMLLoad
13
10
  end
14
11
 
15
12
  def completed?
@@ -22,6 +19,7 @@ class Jobler::Job < ActiveRecord::Base
22
19
 
23
20
  def to_param
24
21
  raise "No slug" unless slug?
22
+
25
23
  slug
26
24
  end
27
25
 
@@ -1,5 +1,7 @@
1
- class Jobler::Result < ActiveRecord::Base
2
- belongs_to :job, class_name: "Jobler::Job"
1
+ class Jobler::Result < ActiveRecord::Base # rubocop:disable Rails/ApplicationRecord
2
+ belongs_to :job, class_name: "Jobler::Job", inverse_of: :results
3
+
4
+ has_one_attached :file
3
5
 
4
6
  validates :job, :name, presence: true
5
7
  end
@@ -4,7 +4,11 @@
4
4
 
5
5
  <% if @job.completed? %>
6
6
  <% if @result.is_a?(Jobler::FileDownload) %>
7
- <%= link_to "Download", download_path(@job) %>
7
+ <% if @result.url.present? %>
8
+ <%= link_to "Download", @result.url %>
9
+ <% else %>
10
+ <%= link_to "Download", download_path(@job) %>
11
+ <% end %>
8
12
  <% elsif @result.is_a?(Jobler::PageRender) %>
9
13
  <%= @result.body.html_safe %>
10
14
  <% end %>
@@ -3,13 +3,15 @@
3
3
  <head>
4
4
  <title>Jobler</title>
5
5
 
6
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
6
8
  <%= stylesheet_link_tag "jobler/application", media: "all" %>
7
- <%= stylesheet_link_tag "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css", integrity: "sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u", crossorigin: "anonymous" %>
8
9
 
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-ujs/1.2.2/rails.min.js" integrity="sha256-BbyWhCn0G+F6xbWJ2pcI5LnnpsnpSzyjJNVtl7ABp+M=" crossorigin="anonymous"></script>
12
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-timeago/1.6.0/jquery.timeago.min.js" integrity="sha256-JAdlnMiYFZRwqQqgiYwLjSkV94QtVzcbvasQMm05THk=" crossorigin="anonymous"></script>
13
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=" crossorigin="anonymous"></script>
9
14
  <%= javascript_include_tag "jobler/application" %>
10
- <%= javascript_include_tag "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js", integrity: "sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa", crossorigin: "anonymous" %>
11
- <%= javascript_include_tag "https://use.fontawesome.com/de02b76735.js" %>
12
- <%= javascript_include_tag "https://cdnjs.cloudflare.com/ajax/libs/jquery-timeago/1.5.4/jquery.timeago.min.js" %>
13
15
 
14
16
  <%= csrf_meta_tags %>
15
17
  </head>
@@ -1,4 +1,4 @@
1
- class CreateJobs < ActiveRecord::Migration
1
+ class CreateJobs < ActiveRecord::Migration[5.1]
2
2
  def change
3
3
  create_table :jobler_jobs do |t|
4
4
  t.string :jobler_type, null: false
@@ -1,4 +1,4 @@
1
- class CreateJoblerResults < ActiveRecord::Migration
1
+ class CreateJoblerResults < ActiveRecord::Migration[5.1]
2
2
  def change
3
3
  create_table :jobler_results do |t|
4
4
  t.belongs_to :job, index: true, null: false
@@ -1,4 +1,4 @@
1
- class AddLocaleToJobs < ActiveRecord::Migration
1
+ class AddLocaleToJobs < ActiveRecord::Migration[5.1]
2
2
  def change
3
3
  add_column :jobler_jobs, :locale, :string
4
4
  end
@@ -1,4 +1,4 @@
1
- class AddErrorMessageAndErrorBacktraceToJobs < ActiveRecord::Migration
1
+ class AddErrorMessageAndErrorBacktraceToJobs < ActiveRecord::Migration[5.1]
2
2
  def change
3
3
  add_column :jobler_jobs, :error_message, :text
4
4
  add_column :jobler_jobs, :error_type, :string
@@ -1,4 +1,4 @@
1
- class AddSlugToJobs < ActiveRecord::Migration
1
+ class AddSlugToJobs < ActiveRecord::Migration[5.1]
2
2
  def change
3
3
  add_column :jobler_jobs, :slug, :string
4
4
  add_index :jobler_jobs, :slug, unique: true
@@ -1,4 +1,4 @@
1
- class AddHostProtocolAndPortToJoblerJobs < ActiveRecord::Migration
1
+ class AddHostProtocolAndPortToJoblerJobs < ActiveRecord::Migration[5.1]
2
2
  def change
3
3
  add_column :jobler_jobs, :host, :string
4
4
  add_column :jobler_jobs, :protocol, :string
@@ -1,2 +1,2 @@
1
- class Jobler::BaseController < ActionController::Base
1
+ class Jobler::BaseController < ActionController::Base # rubocop:disable Rails/ApplicationController
2
2
  end
@@ -1,19 +1,53 @@
1
1
  class Jobler::BaseJobler
2
+ attr_accessor :controller, :format
2
3
  attr_reader :args, :job
3
4
 
4
- def create_result!(args)
5
- if args[:temp_file]
6
- temp_file = args.fetch(:temp_file)
7
- temp_file.close unless temp_file.closed?
8
- content = File.read(temp_file.path)
5
+ def self.before_jobling(&blk)
6
+ @@before_jobling ||= [] # rubocop:disable Style/ClassVars
7
+ @@before_jobling << blk
8
+ end
9
+
10
+ def self.after_jobling(&blk)
11
+ @@after_jobling ||= [] # rubocop:disable Style/ClassVars
12
+ @@after_jobling << blk
13
+ end
14
+
15
+ def initialize(args:, job:)
16
+ @args = args
17
+ @job = job
18
+ end
19
+
20
+ def call_before_callbacks
21
+ @@before_jobling&.each do |before_callback|
22
+ instance_eval(&before_callback)
23
+ end
24
+ end
25
+
26
+ def call_after_callbacks
27
+ @@after_jobling&.each do |after_callback|
28
+ instance_eval(&after_callback)
29
+ end
30
+ end
31
+
32
+ def create_result!(content: nil, name:, result: nil, temp_file: nil, save_in_database: false)
33
+ jobler_result = job.results.new(name: name)
34
+
35
+ if content && !temp_file
36
+ temp_file = Tempfile.new(name)
37
+ temp_file.write(content)
38
+ temp_file.close
39
+ end
40
+
41
+ if result
42
+ jobler_result.result = result
9
43
  else
10
- content = args.fetch(:content)
44
+ raise "No tempfile could be found" unless temp_file
45
+
46
+ handle_file(jobler_result: jobler_result, save_in_database: save_in_database, temp_file: temp_file)
11
47
  end
12
48
 
13
- job.results.create!(
14
- name: args.fetch(:name),
15
- result: content
16
- )
49
+ jobler_result.save!
50
+ jobler_result
17
51
  end
18
52
 
19
53
  def execute!
@@ -46,7 +80,7 @@ class Jobler::BaseJobler
46
80
  end
47
81
 
48
82
  if update
49
- job.update_attributes!(progress: new_progress)
83
+ job.update!(progress: new_progress)
50
84
  @_current_progress = new_progress
51
85
  end
52
86
  end
@@ -56,9 +90,7 @@ class Jobler::BaseJobler
56
90
  end
57
91
 
58
92
  def render(template_path, locals = {})
59
- if template_path.is_a?(Symbol)
60
- template_path = "joblers/#{jobler_name}/#{template_path}"
61
- end
93
+ template_path = "joblers/#{jobler_name}/#{template_path}" if template_path.is_a?(Symbol)
62
94
 
63
95
  request = ActionDispatch::Request.new(
64
96
  "HTTP_HOST" => "#{job.host}:#{job.port}",
@@ -70,7 +102,7 @@ class Jobler::BaseJobler
70
102
  controller.request = request
71
103
  controller.response = ActionDispatch::Response.new
72
104
 
73
- render_result = controller.render(template_path, formats: Mime::EXTENSION_LOOKUP.keys, layout: false, locals: {jobler: self}.merge(locals))
105
+ render_result = controller.render(template_path, layout: false, locals: {jobler: self}.merge(locals))
74
106
 
75
107
  if render_result.is_a?(String)
76
108
  # Rails 5 behaviour
@@ -85,15 +117,37 @@ class Jobler::BaseJobler
85
117
  raise NoMethodError, "You should define the 'result' method on #{self.class.name}"
86
118
  end
87
119
 
88
- def temp_file_for_result(args)
89
- job_result = job.results.where(name: args.fetch(:name)).first
120
+ def temp_file_for_result(name:)
121
+ job_result = job.results.where(name: name).first
122
+ raise "No result by that name: #{name}" unless job_result
90
123
 
91
- raise "No result by that name: #{args.fetch(:name)}" unless job_result
92
-
93
- temp_file = Tempfile.new
124
+ temp_file = ::Tempfile.new("jobler_tempfile")
94
125
  temp_file.binmode
95
126
  temp_file.write(job_result.result)
96
127
  temp_file.close
97
128
  temp_file
98
129
  end
130
+
131
+ def url_for_result(name:)
132
+ job_result = job.results.where(name: name).first
133
+ raise "No result by that name: #{name}" unless job_result
134
+
135
+ Rails.application.routes.url_helpers.rails_blob_path(job_result.file.attachment, only_path: true)
136
+ end
137
+
138
+ private
139
+
140
+ def handle_file(jobler_result:, save_in_database:, temp_file:)
141
+ if save_in_database
142
+ temp_file = temp_file
143
+ temp_file.close unless temp_file.closed?
144
+ content = File.read(temp_file.path)
145
+ jobler_result.result = content
146
+ else
147
+ jobler_result.file.attach(
148
+ filename: File.basename(temp_file.path),
149
+ io: File.open(temp_file.path)
150
+ )
151
+ end
152
+ end
99
153
  end
@@ -1,8 +1,12 @@
1
1
  class Jobler::FileDownload
2
- attr_reader :file_name, :temp_file
2
+ attr_reader :file_name, :temp_file, :url
3
3
 
4
- def initialize(args)
5
- @file_name = args.fetch(:file_name)
6
- @temp_file = args.fetch(:temp_file)
4
+ def initialize(file_name: nil, temp_file: nil, url: nil)
5
+ raise "Temp file or URL should be given" if !temp_file && !url
6
+ raise "No filename given with temp-file" if temp_file && file_name.blank?
7
+
8
+ @file_name = file_name
9
+ @temp_file = temp_file
10
+ @url = url
7
11
  end
8
12
  end
@@ -1,18 +1,24 @@
1
- class Jobler::JobRunner < ActiveJob::Base
1
+ class Jobler::JobRunner < ActiveJob::Base # rubocop:disable Rails/ApplicationJob
2
2
  queue_as :jobler
3
3
 
4
4
  def perform(job_id)
5
5
  @job = Jobler::Job.find(job_id)
6
- @job.update_attributes!(started_at: Time.zone.now, state: "started")
6
+ @job.update!(started_at: Time.zone.now, state: "started")
7
7
 
8
8
  begin
9
9
  with_locale do
10
- @job.jobler.execute!
10
+ @job.jobler.call_before_callbacks
11
+
12
+ begin
13
+ @job.jobler.execute!
14
+ ensure
15
+ @job.jobler.call_after_callbacks
16
+ end
11
17
  end
12
18
 
13
- @job.update_attributes!(ended_at: Time.zone.now, progress: 1.0, state: "completed")
19
+ @job.update!(ended_at: Time.zone.now, progress: 1.0, state: "completed")
14
20
  rescue Exception => e # rubocop:disable Lint/RescueException
15
- @job.update_attributes!(
21
+ @job.update!(
16
22
  ended_at: Time.zone.now,
17
23
  error_message: e.message,
18
24
  error_type: e.class.name,
@@ -1,3 +1,3 @@
1
1
  module Jobler
2
- VERSION = "0.0.8".freeze
2
+ VERSION = "0.0.13".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jobler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - kaspernj
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-10 00:00:00.000000000 Z
11
+ date: 2020-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4'
19
+ version: 6.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '4'
26
+ version: 6.0.0
27
27
  description: Generate pages or files in the background
28
28
  email:
29
29
  - kaspernj@gmail.com
@@ -43,6 +43,7 @@ files:
43
43
  - app/controllers/jobler/downloads_controller.rb
44
44
  - app/controllers/jobler/jobs_controller.rb
45
45
  - app/helpers/jobler/application_helper.rb
46
+ - app/joblers/jobler/models/destroyer_jobler.rb
46
47
  - app/models/jobler/job.rb
47
48
  - app/models/jobler/result.rb
48
49
  - app/views/jobler/jobs/show.html.erb
@@ -84,8 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
85
  - !ruby/object:Gem::Version
85
86
  version: '0'
86
87
  requirements: []
87
- rubyforge_project:
88
- rubygems_version: 2.6.8
88
+ rubygems_version: 3.0.6
89
89
  signing_key:
90
90
  specification_version: 4
91
91
  summary: Generate pages or files in the background